From 555c6a96cf5dbf93baf475f1b962235395b7e943 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Fri, 1 Nov 2024 15:23:07 +1100 Subject: [PATCH 1/3] Separate package version and Git resource functions from create release --- pkg/cmd/release/create/create.go | 590 +---------------- pkg/cmd/release/create/create_test.go | 429 ++++++------- pkg/cmd/release/create/string_utilities.go | 43 -- .../create => gitresources}/gitResources.go | 18 +- .../gitResources_test.go | 217 +++---- pkg/packages/packages.go | 605 ++++++++++++++++++ pkg/util/util.go | 45 +- 7 files changed, 1010 insertions(+), 937 deletions(-) delete mode 100644 pkg/cmd/release/create/string_utilities.go rename pkg/{cmd/release/create => gitresources}/gitResources.go (96%) rename pkg/{cmd/release/create => gitresources}/gitResources_test.go (50%) create mode 100644 pkg/packages/packages.go diff --git a/pkg/cmd/release/create/create.go b/pkg/cmd/release/create/create.go index dd322f00..154897ba 100644 --- a/pkg/cmd/release/create/create.go +++ b/pkg/cmd/release/create/create.go @@ -6,9 +6,6 @@ import ( "fmt" "io" "os" - "regexp" - "sort" - "strings" "time" "github.com/OctopusDeploy/cli/pkg/apiclient" @@ -20,7 +17,9 @@ import ( cliErrors "github.com/OctopusDeploy/cli/pkg/errors" "github.com/OctopusDeploy/cli/pkg/executor" "github.com/OctopusDeploy/cli/pkg/factory" + "github.com/OctopusDeploy/cli/pkg/gitresources" "github.com/OctopusDeploy/cli/pkg/output" + "github.com/OctopusDeploy/cli/pkg/packages" "github.com/OctopusDeploy/cli/pkg/question" "github.com/OctopusDeploy/cli/pkg/question/selectors" "github.com/OctopusDeploy/cli/pkg/surveyext" @@ -347,437 +346,34 @@ func createRun(cmd *cobra.Command, f factory.Factory, flags *CreateFlags) error return nil } -type StepPackageVersion struct { - // these 3 fields are the main ones for showing the user - PackageID string - ActionName string // "StepName is an obsolete alias for ActionName, they always contain the same value" - Version string // note this may be an empty string, indicating that no version could be found for this package yet - - // used to locate the deployment process VersioningStrategy Donor Package - PackageReferenceName string -} - -// BuildPackageVersionBaseline loads the deployment process template from the server, and for each step+package therein, +// BuildPackageVersionBaselineForChannel loads the deployment process template from the server, and for each step+package therein, // finds the latest available version satisfying the channel version rules. Result is the list of step+package+versions // to use as a baseline. The package version override process takes this as an input and layers on top of it -func BuildPackageVersionBaseline(octopus *octopusApiClient.Client, deploymentProcessTemplate *deployments.DeploymentProcessTemplate, channel *channels.Channel) ([]*StepPackageVersion, error) { - result := make([]*StepPackageVersion, 0, len(deploymentProcessTemplate.Packages)) - - // step 1: pass over all the packages in the deployment process, group them - // by their feed, then subgroup by packageId - - // map(key: FeedID, value: list of references using the package so we can trace back to steps) - feedsToQuery := make(map[string][]releases.ReleaseTemplatePackage) - for _, pkg := range deploymentProcessTemplate.Packages { - - if pkg.FixedVersion != "" { - // If a package has a fixed version it shouldn't be displayed or overridable at all - continue - } - - // If a package is not considered resolvable by the server, don't attempt to query it's feed or lookup - // any potential versions for it; we can't succeed in that because variable templates won't get expanded - // until deployment time - if !pkg.IsResolvable { - result = append(result, &StepPackageVersion{ - PackageID: pkg.PackageID, - ActionName: pkg.ActionName, - PackageReferenceName: pkg.PackageReferenceName, - Version: "", - }) - continue - } - if feedPackages, seenFeedBefore := feedsToQuery[pkg.FeedID]; !seenFeedBefore { - feedsToQuery[pkg.FeedID] = []releases.ReleaseTemplatePackage{pkg} - } else { - // seen both the feed and package, but not against this particular step - feedsToQuery[pkg.FeedID] = append(feedPackages, pkg) - } - } - - if len(feedsToQuery) == 0 { - return make([]*StepPackageVersion, 0), nil - } - - // step 2: load the feed resources, so we can get SearchPackageVersionsTemplate - feedIds := make([]string, 0, len(feedsToQuery)) - for k := range feedsToQuery { - feedIds = append(feedIds, k) - } - sort.Strings(feedIds) // we need to sort them otherwise the order is indeterminate. Server doesn't care but our unit tests fail - foundFeeds, err := octopus.Feeds.Get(feeds.FeedsQuery{IDs: feedIds, Take: len(feedIds)}) - if err != nil { - return nil, err - } - - // step 3: for each package within a feed, ask the server to select the best package version for it, applying the channel rules - for _, feed := range foundFeeds.Items { - packageRefsInFeed, ok := feedsToQuery[feed.GetID()] - if !ok { - return nil, errors.New("internal consistency error; feed ID not found in feedsToQuery") // should never happen - } - - cache := make(map[feeds.SearchPackageVersionsQuery]string) // cache value is the package version - - for _, packageRef := range packageRefsInFeed { - query := feeds.SearchPackageVersionsQuery{ - PackageID: packageRef.PackageID, - Take: 1, - } - // look in the channel rules for a version filter for this step+package - rulesLoop: - for _, rule := range channel.Rules { - for _, ap := range rule.ActionPackages { - if ap.PackageReference == packageRef.PackageReferenceName && ap.DeploymentAction == packageRef.ActionName { - // this rule applies to our step/packageref combo - query.PreReleaseTag = rule.Tag - query.VersionRange = rule.VersionRange - // the octopus server won't let the same package be targeted by more than one rule, so - // once we've found the first matching rule for our step+package, we can stop looping - break rulesLoop - } - } - } - - if cachedVersion, ok := cache[query]; ok { - result = append(result, &StepPackageVersion{ - PackageID: packageRef.PackageID, - ActionName: packageRef.ActionName, - PackageReferenceName: packageRef.PackageReferenceName, - Version: cachedVersion, - }) - } else { // uncached; ask the server - versions, err := octopus.Feeds.SearchFeedPackageVersions(feed, query) - if err != nil { - return nil, err - } - - switch len(versions.Items) { - case 0: // no package found; cache the response - cache[query] = "" - result = append(result, &StepPackageVersion{ - PackageID: packageRef.PackageID, - ActionName: packageRef.ActionName, - PackageReferenceName: packageRef.PackageReferenceName, - Version: "", - }) - - case 1: - cache[query] = versions.Items[0].Version - result = append(result, &StepPackageVersion{ - PackageID: packageRef.PackageID, - ActionName: packageRef.ActionName, - PackageReferenceName: packageRef.PackageReferenceName, - Version: versions.Items[0].Version, - }) - - default: - return nil, errors.New("internal error; more than one package returned when only 1 specified") +func BuildPackageVersionBaselineForChannel(octopus *octopusApiClient.Client, deploymentProcessTemplate *deployments.DeploymentProcessTemplate, channel *channels.Channel) ([]*packages.StepPackageVersion, error) { + + result, err := packages.BuildPackageVersionBaseline(octopus, deploymentProcessTemplate.Packages, func(packageRef releases.ReleaseTemplatePackage, query feeds.SearchPackageVersionsQuery) { + // look in the channel rules for a version filter for this step+package + + rulesLoop: + for _, rule := range channel.Rules { + for _, ap := range rule.ActionPackages { + if ap.PackageReference == packageRef.PackageReferenceName && ap.DeploymentAction == packageRef.ActionName { + // this rule applies to our step/packageref combo + query.PreReleaseTag = rule.Tag + query.VersionRange = rule.VersionRange + // the octopus server won't let the same package be targeted by more than one rule, so + // once we've found the first matching rule for our step+package, we can stop looping + break rulesLoop } } } - } - return result, nil -} - -type PackageVersionOverride struct { - ActionName string // optional, but one or both of ActionName or PackageID must be supplied - PackageID string // optional, but one or both of ActionName or PackageID must be supplied - PackageReferenceName string // optional; use for advanced situations where the same package is referenced multiple times by a single step - Version string // required -} - -// ToPackageOverrideString converts the struct back into a string which the server can parse e.g. StepName:Version. -// This is the inverse of ParsePackageOverrideString -func (p *PackageVersionOverride) ToPackageOverrideString() string { - components := make([]string, 0, 3) - - // stepNameOrPackageID always comes first if we have it - if p.PackageID != "" { - components = append(components, p.PackageID) - } else if p.ActionName != "" { // can't have both PackageID and ActionName; PackageID wins - components = append(components, p.ActionName) - } - - // followed by package reference name if we have it - if p.PackageReferenceName != "" { - if len(components) == 0 { // if we have an explicit packagereference but no packageId or action, we need to express it with *:ref:version - components = append(components, "*") - } - components = append(components, p.PackageReferenceName) - } - - if len(components) == 0 { // the server can't deal with just a number by itself; if we want to override everything we must pass *:Version - components = append(components, "*") - } - components = append(components, p.Version) - - return strings.Join(components, ":") -} - -// splitPackageOverrideString splits the input string into components based on delimiter characters. -// we want to pick up empty entries here; so "::5" and ":pterm:5" should both return THREE components, rather than one or two -// and we want to allow for multiple different delimeters. -// neither the builtin golang strings.Split or strings.FieldsFunc support this. Logic borrowed from strings.FieldsFunc with heavy modifications -func splitPackageOverrideString(s string) []string { - return splitString(s, []int32{':', '/', '='}) -} - -// AmbiguousPackageVersionOverride tells us that we want to set the version of some package to `Version` -// but it's not clear whether ActionNameOrPackageID refers to an ActionName or PackageID at this point -type AmbiguousPackageVersionOverride struct { - ActionNameOrPackageID string - PackageReferenceName string - Version string -} - -// taken from here https://github.com/OctopusDeploy/Versioning/blob/main/source/Octopus.Versioning/Octopus/OctopusVersionParser.cs#L29 -// but simplified, and removed the support for optional whitespace around version numbers (OctopusVersion would allow "1 . 2 . 3" whereas we won't -// otherwise this is very lenient -var validVersionRegex, _ = regexp.Compile("(?i)" + `^\s*(v|V)?\d+(\.\d+)?(\.\d+)?(\.\d+)?[.\-_\\]?([a-z0-9]*?)([.\-_\\]([a-z0-9.\-_\\]*?)?)?(\+([a-z0-9_\-.\\+]*?))?$`) - -func isValidVersion(version string) bool { - return validVersionRegex.MatchString(version) -} - -// ParsePackageOverrideString parses a package version override string into a structure. -// Logic should align with PackageVersionResolver in the Octopus Server and .NET CLI -// In cases where things are ambiguous, we look in steps for matching values to see if something is a PackageID or a StepName -func ParsePackageOverrideString(packageOverride string) (*AmbiguousPackageVersionOverride, error) { - if packageOverride == "" { - return nil, errors.New("empty package version specification") - } - - components := splitPackageOverrideString(packageOverride) - packageReferenceName, stepNameOrPackageID, version := "", "", "" - - switch len(components) { - case 2: - // if there are two components it is (StepName|PackageID):Version - stepNameOrPackageID, version = strings.TrimSpace(components[0]), strings.TrimSpace(components[1]) - case 3: - // if there are three components it is (StepName|PackageID):PackageReferenceName:Version - stepNameOrPackageID, packageReferenceName, version = strings.TrimSpace(components[0]), strings.TrimSpace(components[1]), strings.TrimSpace(components[2]) - default: - return nil, fmt.Errorf("package version specification \"%s\" does not use expected format", packageOverride) - } - - // must always specify a version; must specify either packageID, stepName or both - if version == "" { - return nil, fmt.Errorf("package version specification \"%s\" does not use expected format", packageOverride) - } - if !isValidVersion(version) { - return nil, fmt.Errorf("version component \"%s\" is not a valid version", version) - } - - // compensate for wildcards - if packageReferenceName == "*" { - packageReferenceName = "" - } - if stepNameOrPackageID == "*" { - stepNameOrPackageID = "" - } - - return &AmbiguousPackageVersionOverride{ - ActionNameOrPackageID: stepNameOrPackageID, - PackageReferenceName: packageReferenceName, - Version: version, - }, nil -} - -func ResolvePackageOverride(override *AmbiguousPackageVersionOverride, steps []*StepPackageVersion) (*PackageVersionOverride, error) { - // shortcut for wildcard matches; these match everything so we don't need to do any work - if override.PackageReferenceName == "" && override.ActionNameOrPackageID == "" { - return &PackageVersionOverride{ - ActionName: "", - PackageID: "", - PackageReferenceName: "", - Version: override.Version, - }, nil - } - - actionNameOrPackageID := override.ActionNameOrPackageID - - // it could be either a stepname or a package ID; match against the list of packages to try and guess. - // logic matching the server: - // - exact match on stepName + refName - // - then exact match on packageId + refName - // - then match on * + refName - // - then match on stepName + * - // - then match on packageID + * - type match struct { - priority int - actionName string // if set we matched on actionName, else we didn't - packageID string // if set we matched on packageID, else we didn't - packageReferenceName string // if set we matched on packageReferenceName, else we didn't - } - - matches := make([]match, 0, 2) // common case is likely to be 2; if we have a packageID then we may match both exactly and partially on the ID depending on referenceName - for _, p := range steps { - if p.ActionName != "" && p.ActionName == actionNameOrPackageID { - if p.PackageReferenceName == override.PackageReferenceName { - matches = append(matches, match{priority: 100, actionName: p.ActionName, packageReferenceName: p.PackageReferenceName}) - } else { - matches = append(matches, match{priority: 50, actionName: p.ActionName}) - } - } else if p.PackageID != "" && p.PackageID == actionNameOrPackageID { - if p.PackageReferenceName == override.PackageReferenceName { - matches = append(matches, match{priority: 90, packageID: p.PackageID, packageReferenceName: p.PackageReferenceName}) - } else { - matches = append(matches, match{priority: 40, packageID: p.PackageID}) - } - } else if p.PackageReferenceName != "" && p.PackageReferenceName == override.PackageReferenceName { - matches = append(matches, match{priority: 80, packageReferenceName: p.PackageReferenceName}) - } - } - - if len(matches) == 0 { - return nil, fmt.Errorf("could not resolve step name or package matching %s", actionNameOrPackageID) - } - sort.SliceStable(matches, func(i, j int) bool { // want a stable sort so if there's more than one possible match we pick the first one - return matches[i].priority > matches[j].priority }) - return &PackageVersionOverride{ - ActionName: matches[0].actionName, - PackageID: matches[0].packageID, - PackageReferenceName: matches[0].packageReferenceName, - Version: override.Version, - }, nil -} - -func ApplyPackageOverrides(packages []*StepPackageVersion, overrides []*PackageVersionOverride) []*StepPackageVersion { - for _, o := range overrides { - packages = applyPackageOverride(packages, o) - } - return packages -} - -func applyPackageOverride(packages []*StepPackageVersion, override *PackageVersionOverride) []*StepPackageVersion { - if override.Version == "" { - return packages // not specifying a version is technically an error, but we'll just no-op it for safety; should have been filtered out by ParsePackageOverrideString before we get here - } - - var matcher func(pkg *StepPackageVersion) bool = nil - - switch { - case override.PackageID == "" && override.ActionName == "": // match everything - matcher = func(pkg *StepPackageVersion) bool { - return true - } - case override.PackageID != "" && override.ActionName == "": // match on package ID only - matcher = func(pkg *StepPackageVersion) bool { - return pkg.PackageID == override.PackageID - } - case override.PackageID == "" && override.ActionName != "": // match on step only - matcher = func(pkg *StepPackageVersion) bool { - return pkg.ActionName == override.ActionName - } - case override.PackageID != "" && override.ActionName != "": // match on both; shouldn't be possible but let's ensure it works anyway - matcher = func(pkg *StepPackageVersion) bool { - return pkg.PackageID == override.PackageID && pkg.ActionName == override.ActionName - } - } - - if override.PackageReferenceName != "" { // must also match package reference name - if matcher == nil { - matcher = func(pkg *StepPackageVersion) bool { - return pkg.PackageReferenceName == override.PackageReferenceName - } - } else { - prevMatcher := matcher - matcher = func(pkg *StepPackageVersion) bool { - return pkg.PackageReferenceName == override.PackageReferenceName && prevMatcher(pkg) - } - } - } - - if matcher == nil { - return packages // we can't possibly match against anything; no-op. Should have been filtered out by ParsePackageOverrideString - } - - result := make([]*StepPackageVersion, len(packages)) - for i, p := range packages { - if matcher(p) { - result[i] = &StepPackageVersion{ - PackageID: p.PackageID, - ActionName: p.ActionName, - PackageReferenceName: p.PackageReferenceName, - Version: override.Version, // Important bit - } - } else { - result[i] = p - } - } - return result -} - -// Note this always uses the Table Printer, it pays no respect to outputformat=json, because it's only part of the interactive flow -func printPackageVersions(ioWriter io.Writer, packages []*StepPackageVersion) error { - // step 1: consolidate multiple rows - consolidated := make([]*StepPackageVersion, 0, len(packages)) - for _, pkg := range packages { - - // the common case is that packageReferenceName will be equal to PackageID. - // however, in advanced cases it may not be, so we need to do extra work to show the packageReferenceName too. - // we suffix it onto the step name, following the web UI - qualifiedPkgActionName := pkg.ActionName - if pkg.PackageID != pkg.PackageReferenceName { - //qualifiedPkgActionName = fmt.Sprintf("%s%s", qualifiedPkgActionName, output.Yellowf("/%s", pkg.PackageReferenceName)) - qualifiedPkgActionName = fmt.Sprintf("%s/%s", qualifiedPkgActionName, pkg.PackageReferenceName) - } else { - qualifiedPkgActionName = fmt.Sprintf("%s%s", qualifiedPkgActionName, output.Dimf("/%s", pkg.PackageReferenceName)) - } - - // find existing entry and insert row below it - updatedExisting := false - for index, entry := range consolidated { - if entry.PackageID == pkg.PackageID && entry.Version == pkg.Version { - consolidated = append(consolidated[:index+2], consolidated[index+1:]...) - consolidated[index+1] = &StepPackageVersion{ - PackageID: output.Dim(pkg.PackageID), - Version: output.Dim(pkg.Version), - ActionName: qualifiedPkgActionName, - } - updatedExisting = true - break - } - } - if !updatedExisting { - consolidated = append(consolidated, &StepPackageVersion{ - PackageID: pkg.PackageID, - Version: pkg.Version, - ActionName: qualifiedPkgActionName, - }) - } - } - - // step 2: print them - t := output.NewTable(ioWriter) - t.AddRow( - output.Bold("PACKAGE"), - output.Bold("VERSION"), - output.Bold("STEP NAME/PACKAGE REFERENCE"), - ) - //t.AddRow( - // "-------", - // "-------", - // "---------------------------", - //) - - for _, pkg := range consolidated { - version := pkg.Version - if version == "" { - version = output.Yellow("unknown") // can't determine version for this package - } - t.AddRow( - pkg.PackageID, - version, - pkg.ActionName, - ) + if err != nil { + return nil, err } - return t.Print() + return result, nil } func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker question.Asker, options *executor.TaskOptionsCreateRelease) error { @@ -876,14 +472,14 @@ func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker ques return err } - packageVersionBaseline, err := BuildPackageVersionBaseline(octopus, deploymentProcessTemplate, selectedChannel) + packageVersionBaseline, err := BuildPackageVersionBaselineForChannel(octopus, deploymentProcessTemplate, selectedChannel) if err != nil { return err } - var overriddenPackageVersions []*StepPackageVersion + var overriddenPackageVersions []*packages.StepPackageVersion if len(packageVersionBaseline) > 0 { // if we have packages, run the package flow - opv, packageVersionOverrides, err := AskPackageOverrideLoop( + opv, packageVersionOverrides, err := packages.AskPackageOverrideLoop( packageVersionBaseline, options.DefaultPackageVersion, options.PackageVersionOverrides, @@ -905,10 +501,10 @@ func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker ques overriddenPackageVersions = packageVersionBaseline // there aren't any, but satisfy the code below anyway } - gitResourcesBaseline := BuildGitResourcesBaseline(deploymentProcessTemplate) + gitResourcesBaseline := gitresources.BuildGitResourcesBaseline(deploymentProcessTemplate.GitResources) if len(gitResourcesBaseline) > 0 { - overriddenGitResources, err := AskGitResourceOverrideLoop( + overriddenGitResources, err := gitresources.AskGitResourceOverrideLoop( gitResourcesBaseline, options.GitResourceRefs, asker, @@ -949,7 +545,7 @@ func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker ques if versioningStrategy.DonorPackageStepID != nil || versioningStrategy.DonorPackage != nil { // we've already done the package version work so we can just ask the donor package which version it has selected - var donorPackage *StepPackageVersion + var donorPackage *packages.StepPackageVersion for _, pkg := range overriddenPackageVersions { if pkg.PackageReferenceName == versioningStrategy.DonorPackage.PackageReference && pkg.ActionName == versioningStrategy.DonorPackage.DeploymentAction { donorPackage = pkg @@ -992,138 +588,6 @@ func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker ques return nil } -func AskPackageOverrideLoop( - packageVersionBaseline []*StepPackageVersion, - defaultPackageVersion string, // the --package-version command line flag - initialPackageOverrideFlags []string, // the --package command line flag (multiple occurrences) - asker question.Asker, - stdout io.Writer) ([]*StepPackageVersion, []*PackageVersionOverride, error) { - packageVersionOverrides := make([]*PackageVersionOverride, 0) - - // pickup any partial package specifications that may have arrived on the commandline - if defaultPackageVersion != "" { - // blind apply to everything - packageVersionOverrides = append(packageVersionOverrides, &PackageVersionOverride{Version: defaultPackageVersion}) - } - - for _, s := range initialPackageOverrideFlags { - ambOverride, err := ParsePackageOverrideString(s) - if err != nil { - continue // silently ignore anything that wasn't parseable (should we emit a warning?) - } - resolvedOverride, err := ResolvePackageOverride(ambOverride, packageVersionBaseline) - if err != nil { - continue // silently ignore anything that wasn't parseable (should we emit a warning?) - } - packageVersionOverrides = append(packageVersionOverrides, resolvedOverride) - } - - overriddenPackageVersions := ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) - -outerLoop: - for { - err := printPackageVersions(stdout, overriddenPackageVersions) - if err != nil { - return nil, nil, err - } - - // While there are any unresolved package versions, force those - for _, pkgVersionEntry := range overriddenPackageVersions { - if strings.TrimSpace(pkgVersionEntry.Version) == "" { - - var answer = "" - for strings.TrimSpace(answer) == "" { // if they enter a blank line just ask again repeatedly - err = asker(&survey.Input{ - Message: output.Yellowf("Unable to find a version for \"%s\". Specify a version:", pkgVersionEntry.PackageID), - }, &answer, survey.WithValidator(func(ans interface{}) error { - str, ok := ans.(string) - if !ok { - return errors.New("internal error; answer was not a string") - } - if !isValidVersion(str) { - return fmt.Errorf("\"%s\" is not a valid version", str) - } - return nil - })) - - if err != nil { - return nil, nil, err - } - } - - override := &PackageVersionOverride{Version: answer, ActionName: pkgVersionEntry.ActionName, PackageReferenceName: pkgVersionEntry.PackageReferenceName} - if override != nil { - packageVersionOverrides = append(packageVersionOverrides, override) - overriddenPackageVersions = ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) - } - continue outerLoop - } - } - - // After all packages have versions attached, we can let people freely tweak things until they're happy - - // side-channel return value from the validator - var resolvedOverride *PackageVersionOverride = nil - var answer = "" - err = asker(&survey.Input{ - Message: "Package override string (y to accept, u to undo, ? for help):", - }, &answer, survey.WithValidator(func(ans interface{}) error { - str, ok := ans.(string) - if !ok { - return errors.New("internal error; answer was not a string") - } - - switch str { - // valid response for continuing the loop; don't attempt to validate these - case "y", "u", "r", "?", "": - return nil - } - - ambOverride, err := ParsePackageOverrideString(str) - if err != nil { - return err - } - resolvedOverride, err = ResolvePackageOverride(ambOverride, packageVersionBaseline) - if err != nil { - return err - } - - return nil // good! - })) - - // if validators return an error, survey retries itself; the errors don't end up at this level. - if err != nil { - return nil, nil, err - } - - switch answer { - case "y": // YES these are the packages they want - break outerLoop - case "?": // help text - _, _ = fmt.Fprintf(stdout, output.FormatDoc(packageOverrideLoopHelpText)) - case "u": // undo! - if len(packageVersionOverrides) > 0 { - packageVersionOverrides = packageVersionOverrides[:len(packageVersionOverrides)-1] - // always reset to the baseline and apply everything in order, there's less room for logic errors - overriddenPackageVersions = ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) - } - case "r": // reset! All the way back to the calculated versions, discarding even the stuff that came in from the cmdline - if len(packageVersionOverrides) > 0 { - packageVersionOverrides = make([]*PackageVersionOverride, 0) - overriddenPackageVersions = ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) - } - default: - if resolvedOverride != nil { - packageVersionOverrides = append(packageVersionOverrides, resolvedOverride) - // always reset to the baseline and apply everything in order, there's less room for logic errors - overriddenPackageVersions = ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) - } - } - // loop around and let them put in more input - } - return overriddenPackageVersions, packageVersionOverrides, nil -} - func askVersion(ask question.Asker, defaultVersion string) (string, error) { var result string if err := ask(&survey.Input{ diff --git a/pkg/cmd/release/create/create_test.go b/pkg/cmd/release/create/create_test.go index d6ebdc18..240a79b7 100644 --- a/pkg/cmd/release/create/create_test.go +++ b/pkg/cmd/release/create/create_test.go @@ -12,6 +12,7 @@ import ( "github.com/OctopusDeploy/cli/pkg/cmd/release/create" cmdRoot "github.com/OctopusDeploy/cli/pkg/cmd/root" "github.com/OctopusDeploy/cli/pkg/executor" + "github.com/OctopusDeploy/cli/pkg/packages" "github.com/OctopusDeploy/cli/pkg/surveyext" "github.com/OctopusDeploy/cli/test/fixtures" "github.com/OctopusDeploy/cli/test/testutil" @@ -21,7 +22,7 @@ import ( "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/credentials" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/deployments" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/packages" + octopusPackages "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/packages" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/releases" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/resources" @@ -213,8 +214,8 @@ func TestReleaseCreate_AskQuestions_RegularProject(t *testing.T) { }}) // then it will look for versions - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{{PackageID: "pterm", Version: "0.12.51"}}, + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{{PackageID: "pterm", Version: "0.12.51"}}, }) _ = qa.ExpectQuestion(t, &survey.Input{ @@ -260,7 +261,7 @@ func TestReleaseCreate_AskQuestions_RegularProject(t *testing.T) { var fireProject2 = *fireProject // clone the struct value fireProject2.VersioningStrategy = &projects.VersioningStrategy{ - DonorPackage: &packages.DeploymentActionPackage{ + DonorPackage: &octopusPackages.DeploymentActionPackage{ DeploymentAction: "Verify", PackageReference: "nuget-on-verify", }, @@ -318,11 +319,11 @@ func TestReleaseCreate_AskQuestions_RegularProject(t *testing.T) { }}) // then it will look for versions - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{{PackageID: "pterm", Version: "0.12.51"}}, // extra package to prove it's not just picking the first one + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{{PackageID: "pterm", Version: "0.12.51"}}, // extra package to prove it's not just picking the first one }) - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{{PackageID: "NuGet.CommandLine", Version: "6.2.1"}}, // the proper package + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{{PackageID: "NuGet.CommandLine", Version: "6.2.1"}}, // the proper package }) q := qa.ExpectQuestion(t, &survey.Input{ @@ -371,7 +372,7 @@ func TestReleaseCreate_AskQuestions_RegularProject(t *testing.T) { var fireProject2 = *fireProject // clone the struct value fireProject2.VersioningStrategy = &projects.VersioningStrategy{ - DonorPackage: &packages.DeploymentActionPackage{ + DonorPackage: &octopusPackages.DeploymentActionPackage{ DeploymentAction: "Verify", PackageReference: "nuget-on-verify", }, @@ -414,8 +415,8 @@ func TestReleaseCreate_AskQuestions_RegularProject(t *testing.T) { }}) // then it will look for versions - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{{PackageID: "NuGet.CommandLine", Version: "6.2.1"}}, // the proper package + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{{PackageID: "NuGet.CommandLine", Version: "6.2.1"}}, // the proper package }) _ = qa.ExpectQuestion(t, &survey.Input{ @@ -704,7 +705,7 @@ func TestReleaseCreate_AskQuestions_VersionControlledProject(t *testing.T) { func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { - baseline := []*create.StepPackageVersion{ + baseline := []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "6.1.2"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, @@ -716,8 +717,8 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { }{ // this is the happy path where the CLI presents the list of server-selected packages and they just go 'yep' {"no-op test", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("y") @@ -725,12 +726,12 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) assert.Equal(t, baseline, versions) - assert.Equal(t, make([]*create.PackageVersionOverride, 0), overrides) + assert.Equal(t, make([]*packages.PackageVersionOverride, 0), overrides) }}, {"override package based on package ID", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("pterm:2.5") @@ -739,19 +740,19 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "6.1.2"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {PackageID: "pterm", Version: "2.5"}, }, overrides) }}, {"override package based on step name", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("Install:2.5") @@ -760,19 +761,19 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "2.5"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {ActionName: "Install", Version: "2.5"}, }, overrides) }}, {"override package based on package reference", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("Install:pterm:2.5") @@ -781,12 +782,12 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "6.1.2"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {PackageReferenceName: "pterm", ActionName: "Install", Version: "2.5"}, }, overrides) }}, @@ -794,20 +795,20 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { {"entering the loop with --package-version picked up from the command line", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { defaultPackageVersion := "2.5" - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, defaultPackageVersion, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, defaultPackageVersion, make([]string, 0), qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("y") versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "2.5"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {Version: "2.5"}, // TODO the "regenerate command line flags" code is going to re-interpret this as "--package *:2.5" rather than the input which was "--package-version 2.5". Does that matter? }, overrides) }}, @@ -815,20 +816,20 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { {"entering the loop with --package picked up from the command line", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { cmdlinePackages := []string{"Install:pterm:2.5", "NuGet.CommandLine:7.1"} - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", cmdlinePackages, qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", cmdlinePackages, qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("y") versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "7.1"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {PackageReferenceName: "pterm", ActionName: "Install", Version: "2.5"}, {PackageID: "NuGet.CommandLine", Version: "7.1"}, }, overrides) @@ -838,20 +839,20 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { defaultPackageVersion := "9.9" cmdlinePackages := []string{"Install:pterm:2.5", "NuGet.CommandLine:7.1"} - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, defaultPackageVersion, cmdlinePackages, qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, defaultPackageVersion, cmdlinePackages, qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("y") versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "7.1"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "9.9"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {Version: "9.9"}, // TODO the "regenerate command line flags" code is going to re-interpret this as "--package *:9.9" rather than the input which was "--package-version 9.9". Does that matter? {PackageReferenceName: "pterm", ActionName: "Install", Version: "2.5"}, {PackageID: "NuGet.CommandLine", Version: "7.1"}, @@ -859,8 +860,8 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { }}, {"blank answer retries the question", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) validationErr := qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("") @@ -875,12 +876,12 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) assert.Equal(t, baseline, versions) - assert.Equal(t, make([]*create.PackageVersionOverride, 0), overrides) + assert.Equal(t, make([]*packages.PackageVersionOverride, 0), overrides) }}, {"can't specify garbage; question loop retries", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) q := qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}) @@ -905,19 +906,19 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "2.5"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {Version: "2.5"}, }, overrides) }}, {"can't specify packages or steps that aren't there due to validator; question loop retries", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) q := qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}) @@ -933,19 +934,19 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "2.5"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {Version: "2.5"}, }, overrides) }}, {"question loop doesn't retry if it gets a hard error", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWithError(errors.New("hard fail")) @@ -957,8 +958,8 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { }}, {"multiple overrides with undo", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("NuGet.CommandLine:7.1") @@ -973,20 +974,20 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "7.1"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, // this would have been hit by pterm:35 but we undid it }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {PackageID: "NuGet.CommandLine", Version: "7.1"}, {PackageReferenceName: "pterm", ActionName: "Install", Version: "2.5"}, }, overrides) }}, {"multiple overrides with reset", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baseline, "", make([]string, 0), qa.AsAsker(), stdout) }) _ = qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}).AnswerWith("NuGet.CommandLine:7.1") @@ -1001,26 +1002,26 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "2.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "6.1.2"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, // this would have been hit by pterm:35 but we undid it }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {PackageReferenceName: "pterm", ActionName: "Install", Version: "2.5"}, }, overrides) }}, // this is the happy path where the CLI presents the list of server-selected packages and they just go 'yep' {"if we enter the loop with any unresolved packages, force version selection for them before entering the main loop", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - baselineSomeUnresolved := []*create.StepPackageVersion{ + baselineSomeUnresolved := []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: ""}, // unresolved {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: ""}, // unresolved {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, } - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baselineSomeUnresolved, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baselineSomeUnresolved, "", make([]string, 0), qa.AsAsker(), stdout) }) q := qa.ExpectQuestion(t, &survey.Input{Message: "Unable to find a version for \"pterm\". Specify a version:"}) @@ -1055,25 +1056,25 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "75"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "1.0.0"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {PackageReferenceName: "pterm", ActionName: "Install", Version: "75"}, // fully qualify packagereference+actionname to be sure {PackageReferenceName: "NuGet.CommandLine", ActionName: "Install", Version: "1.0.0"}, }, overrides) }}, {"if we enter the loop with any unresolved packages, forced version selection doesn't accept bad input", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - baselineSomeUnresolved := []*create.StepPackageVersion{ + baselineSomeUnresolved := []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: ""}, // unresolved {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, } - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baselineSomeUnresolved, "", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baselineSomeUnresolved, "", make([]string, 0), qa.AsAsker(), stdout) }) q := qa.ExpectQuestion(t, &survey.Input{Message: "Unable to find a version for \"pterm\". Specify a version:"}) @@ -1099,24 +1100,24 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "25.0"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {PackageReferenceName: "pterm", ActionName: "Install", Version: "25.0"}, // fully qualify packagereference+actionname to be sure }, overrides) }}, {"if we enter the loop with any unresolved packages, pick up --package-version before assuming they're unresolved", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - baselineSomeUnresolved := []*create.StepPackageVersion{ + baselineSomeUnresolved := []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: ""}, // unresolved {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: ""}, // unresolved {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, } - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baselineSomeUnresolved, "12.7.5", make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baselineSomeUnresolved, "12.7.5", make([]string, 0), qa.AsAsker(), stdout) }) q := qa.ExpectQuestion(t, &survey.Input{Message: packageOverrideQuestion}) @@ -1131,26 +1132,26 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "12.7.5"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "12.7.5"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "12.7.5"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {Version: "12.7.5"}, // the --package-version input produces this as the first 'override' // and that's all we did there }, overrides) }}, {"if we enter the loop with any unresolved packages, pick up --package before assuming they're unresolved", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - baselineSomeUnresolved := []*create.StepPackageVersion{ + baselineSomeUnresolved := []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: ""}, // unresolved {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: ""}, // unresolved {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, } - receiver := testutil.GoBegin3(func() ([]*create.StepPackageVersion, []*create.PackageVersionOverride, error) { - return create.AskPackageOverrideLoop(baselineSomeUnresolved, "", []string{"Install:pterm:12.9.2"}, qa.AsAsker(), stdout) + receiver := testutil.GoBegin3(func() ([]*packages.StepPackageVersion, []*packages.PackageVersionOverride, error) { + return packages.AskPackageOverrideLoop(baselineSomeUnresolved, "", []string{"Install:pterm:12.9.2"}, qa.AsAsker(), stdout) }) q := qa.ExpectQuestion(t, &survey.Input{Message: "Unable to find a version for \"NuGet.CommandLine\". Specify a version:"}) @@ -1175,12 +1176,12 @@ func TestReleaseCreate_AskQuestions_AskPackageOverrideLoop(t *testing.T) { versions, overrides, err := testutil.ReceiveTriple(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {ActionName: "Install", PackageID: "pterm", PackageReferenceName: "pterm", Version: "12.9.2"}, {ActionName: "Install", PackageID: "NuGet.CommandLine", PackageReferenceName: "NuGet.CommandLine", Version: "75"}, {ActionName: "Verify", PackageID: "pterm", PackageReferenceName: "pterm", Version: "0.12"}, }, versions) - assert.Equal(t, []*create.PackageVersionOverride{ + assert.Equal(t, []*packages.PackageVersionOverride{ {PackageReferenceName: "pterm", ActionName: "Install", Version: "12.9.2"}, // input commandline switch produces this output {PackageReferenceName: "NuGet.CommandLine", ActionName: "Install", Version: "75"}, // our first question produces this }, overrides) @@ -1857,10 +1858,10 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { channel := fixtures.NewChannel(spaceID, "Channels-1", "Default", "Projects-1") - receiver := testutil.GoBegin2(func() ([]*create.StepPackageVersion, error) { + receiver := testutil.GoBegin2(func() ([]*packages.StepPackageVersion, error) { defer api.Close() octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "") - return create.BuildPackageVersionBaseline(octopus, processTemplate, channel) + return create.BuildPackageVersionBaselineForChannel(octopus, processTemplate, channel) }) // octopusApiClient.NewClient fetches the root resource but otherwise BuildPackageVersionBaseline does nothing @@ -1869,7 +1870,7 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { packageVersions, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{}, packageVersions) + assert.Equal(t, []*packages.StepPackageVersion{}, packageVersions) }) t.Run("builds list for single package/step", func(t *testing.T) { @@ -1889,10 +1890,10 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { channel := fixtures.NewChannel(spaceID, "Channels-1", "Default", "Projects-1") - receiver := testutil.GoBegin2(func() ([]*create.StepPackageVersion, error) { + receiver := testutil.GoBegin2(func() ([]*packages.StepPackageVersion, error) { defer api.Close() octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "") - return create.BuildPackageVersionBaseline(octopus, processTemplate, channel) + return create.BuildPackageVersionBaselineForChannel(octopus, processTemplate, channel) }) api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource) @@ -1908,15 +1909,15 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { }}) // now it will search for the package versions - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{ + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{ {PackageID: "pterm", Version: "0.12.51"}, }, }) packageVersions, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ { PackageID: "pterm", ActionName: "Install", @@ -1944,10 +1945,10 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { channel := fixtures.NewChannel(spaceID, "Channels-1", "Default", "Projects-1") - receiver := testutil.GoBegin2(func() ([]*create.StepPackageVersion, error) { + receiver := testutil.GoBegin2(func() ([]*packages.StepPackageVersion, error) { defer api.Close() octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "") - return create.BuildPackageVersionBaseline(octopus, processTemplate, channel) + return create.BuildPackageVersionBaselineForChannel(octopus, processTemplate, channel) }) api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource) @@ -1955,7 +1956,7 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { packageVersions, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{}, packageVersions) + assert.Equal(t, []*packages.StepPackageVersion{}, packageVersions) }) t.Run("builds list containing only selectable package/step when a different step has a fixed package version", func(t *testing.T) { @@ -1983,10 +1984,10 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { channel := fixtures.NewChannel(spaceID, "Channels-1", "Default", "Projects-1") - receiver := testutil.GoBegin2(func() ([]*create.StepPackageVersion, error) { + receiver := testutil.GoBegin2(func() ([]*packages.StepPackageVersion, error) { defer api.Close() octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "") - return create.BuildPackageVersionBaseline(octopus, processTemplate, channel) + return create.BuildPackageVersionBaselineForChannel(octopus, processTemplate, channel) }) api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource) @@ -2002,15 +2003,15 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { }}) // now it will search for the package versions - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{ + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{ {PackageID: "pterm", Version: "0.12.51"}, }, }) packageVersions, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ { PackageID: "pterm", ActionName: "Install", @@ -2058,10 +2059,10 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { channel := fixtures.NewChannel(spaceID, "Channels-1", "Default", "Projects-1") - receiver := testutil.GoBegin2(func() ([]*create.StepPackageVersion, error) { + receiver := testutil.GoBegin2(func() ([]*packages.StepPackageVersion, error) { defer api.Close() octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "") - return create.BuildPackageVersionBaseline(octopus, processTemplate, channel) + return create.BuildPackageVersionBaselineForChannel(octopus, processTemplate, channel) }) api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource) @@ -2082,14 +2083,14 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { }}) // now it will search for the package versions - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{ + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{ {PackageID: "pterm", Version: "0.12.51"}, }, }) // even though two steps use pterm, they're the same so we don't need to ask the server twice - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/Feeds-1001/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{ + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/Feeds-1001/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{ {PackageID: "NuGet.CommandLine", Version: "6.1.2"}, }, }) @@ -2097,7 +2098,7 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { packageVersions, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ { PackageID: "pterm", ActionName: "Install", @@ -2166,7 +2167,7 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { { Tag: "^pre$", VersionRange: "[5.0,6.0)", - ActionPackages: []packages.DeploymentActionPackage{ + ActionPackages: []octopusPackages.DeploymentActionPackage{ {DeploymentAction: "Install", PackageReference: "pterm-on-NOSUCHSTEP"}, // this should be ignored as PackageReference doesn't match {DeploymentAction: "Cleanup", PackageReference: "nuget-on-cleanup"}, // this should match }, @@ -2175,16 +2176,16 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { { Tag: "^$", VersionRange: "[9.9]", - ActionPackages: []packages.DeploymentActionPackage{ + ActionPackages: []octopusPackages.DeploymentActionPackage{ {DeploymentAction: "InstallXYZ", PackageReference: "pterm-on-install"}, // this should be ignored as DeploymentAction doesn't match }, }, } - receiver := testutil.GoBegin2(func() ([]*create.StepPackageVersion, error) { + receiver := testutil.GoBegin2(func() ([]*packages.StepPackageVersion, error) { defer api.Close() octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "") - return create.BuildPackageVersionBaseline(octopus, processTemplate, channel) + return create.BuildPackageVersionBaselineForChannel(octopus, processTemplate, channel) }) api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource) @@ -2205,27 +2206,27 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { }}) // now it will search for the package versions - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{ + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{ {PackageID: "pterm", Version: "0.12.51"}, }, }) // even though two steps use pterm, they're the same so we don't need to ask the server twice - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/Feeds-1001/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{ + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/Feeds-1001/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{ {PackageID: "NuGet.CommandLine", Version: "6.1.2"}, }, }) // second request asks for different filters due to channel rules - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/Feeds-1001/packages/versions?packageId=NuGet.CommandLine&preReleaseTag=%5Epre%24&take=1&versionRange=%5B5.0%2C6.0%29").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{ + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/Feeds-1001/packages/versions?packageId=NuGet.CommandLine&preReleaseTag=%5Epre%24&take=1&versionRange=%5B5.0%2C6.0%29").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{ {PackageID: "NuGet.CommandLine", Version: "5.4.1-prerelease"}, }, }) packageVersions, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ { PackageID: "pterm", ActionName: "Install", @@ -2267,10 +2268,10 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { channel := fixtures.NewChannel(spaceID, "Channels-1", "Default", "Projects-1") - receiver := testutil.GoBegin2(func() ([]*create.StepPackageVersion, error) { + receiver := testutil.GoBegin2(func() ([]*packages.StepPackageVersion, error) { defer api.Close() octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "") - return create.BuildPackageVersionBaseline(octopus, processTemplate, channel) + return create.BuildPackageVersionBaselineForChannel(octopus, processTemplate, channel) }) api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource) @@ -2286,16 +2287,16 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { }}) // now it will search for the package versions - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{}, // empty! + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{}, // empty! }) - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{}, // empty! + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=NuGet.CommandLine&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{}, // empty! }) packageVersions, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ { PackageID: "pterm", ActionName: "Install", @@ -2325,10 +2326,10 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { channel := fixtures.NewChannel(spaceID, "Channels-1", "Default", "Projects-1") - receiver := testutil.GoBegin2(func() ([]*create.StepPackageVersion, error) { + receiver := testutil.GoBegin2(func() ([]*packages.StepPackageVersion, error) { defer api.Close() octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "") - return create.BuildPackageVersionBaseline(octopus, processTemplate, channel) + return create.BuildPackageVersionBaselineForChannel(octopus, processTemplate, channel) }) api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource) @@ -2344,14 +2345,14 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { }}) // now it will search for the package versions (the first one said resolvable:true so we ask the server, even though there's a variable template) - api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm-%23%7BOctopus.Environment.Id%7D&take=1").RespondWith(&resources.Resources[*packages.PackageVersion]{ - Items: []*packages.PackageVersion{}, // empty! + api.ExpectRequest(t, "GET", "/api/Spaces-1/feeds/feeds-builtin/packages/versions?packageId=pterm-%23%7BOctopus.Environment.Id%7D&take=1").RespondWith(&resources.Resources[*octopusPackages.PackageVersion]{ + Items: []*octopusPackages.PackageVersion{}, // empty! }) // the other two packages have IsResolvable:false so we don't even try to look for them. packageVersions, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ { PackageID: "pterm-#{Octopus.Environment.Name}", ActionName: "Install", @@ -2378,19 +2379,19 @@ func TestReleaseCreate_BuildPackageVersionBaseline(t *testing.T) { func TestReleaseCreate_ToPackageOverrideString(t *testing.T) { tests := []struct { name string - input *create.PackageVersionOverride + input *packages.PackageVersionOverride expect string }{ - {name: "ver-only", input: &create.PackageVersionOverride{Version: "0.12"}, expect: "*:0.12"}, - {name: "action-ver", input: &create.PackageVersionOverride{ActionName: "Install", Version: "0.12"}, expect: "Install:0.12"}, - {name: "action-ver-2", input: &create.PackageVersionOverride{ActionName: "Verify", Version: "6.1.2-beta"}, expect: "Verify:6.1.2-beta"}, - {name: "pkg-ver", input: &create.PackageVersionOverride{PackageID: "pterm", Version: "0.12"}, expect: "pterm:0.12"}, - {name: "pkg-ver-2", input: &create.PackageVersionOverride{PackageID: "NuGet.CommandLine", Version: "6.1.2-beta"}, expect: "NuGet.CommandLine:6.1.2-beta"}, - {name: "pkg-action-ver", input: &create.PackageVersionOverride{PackageID: "pterm", ActionName: "Install", Version: "0.12"}, expect: "pterm:0.12"}, // this isn't valid, but if it did happen it should pick packageID - {name: "pkg-ref-ver", input: &create.PackageVersionOverride{PackageReferenceName: "pterm-on-install", PackageID: "pterm", Version: "6.1.2"}, expect: "pterm:pterm-on-install:6.1.2"}, - {name: "action-ref-ver", input: &create.PackageVersionOverride{PackageReferenceName: "pterm-on-install", ActionName: "Install", Version: "6.1.2"}, expect: "Install:pterm-on-install:6.1.2"}, - {name: "star-ref-ver", input: &create.PackageVersionOverride{PackageReferenceName: "pterm-on-install", Version: "6.1.2"}, expect: "*:pterm-on-install:6.1.2"}, - {name: "pkg-action-ref-ver", input: &create.PackageVersionOverride{PackageReferenceName: "pterm", PackageID: "pterm", ActionName: "Install", Version: "1.2.3"}, expect: "pterm:pterm:1.2.3"}, + {name: "ver-only", input: &packages.PackageVersionOverride{Version: "0.12"}, expect: "*:0.12"}, + {name: "action-ver", input: &packages.PackageVersionOverride{ActionName: "Install", Version: "0.12"}, expect: "Install:0.12"}, + {name: "action-ver-2", input: &packages.PackageVersionOverride{ActionName: "Verify", Version: "6.1.2-beta"}, expect: "Verify:6.1.2-beta"}, + {name: "pkg-ver", input: &packages.PackageVersionOverride{PackageID: "pterm", Version: "0.12"}, expect: "pterm:0.12"}, + {name: "pkg-ver-2", input: &packages.PackageVersionOverride{PackageID: "NuGet.CommandLine", Version: "6.1.2-beta"}, expect: "NuGet.CommandLine:6.1.2-beta"}, + {name: "pkg-action-ver", input: &packages.PackageVersionOverride{PackageID: "pterm", ActionName: "Install", Version: "0.12"}, expect: "pterm:0.12"}, // this isn't valid, but if it did happen it should pick packageID + {name: "pkg-ref-ver", input: &packages.PackageVersionOverride{PackageReferenceName: "pterm-on-install", PackageID: "pterm", Version: "6.1.2"}, expect: "pterm:pterm-on-install:6.1.2"}, + {name: "action-ref-ver", input: &packages.PackageVersionOverride{PackageReferenceName: "pterm-on-install", ActionName: "Install", Version: "6.1.2"}, expect: "Install:pterm-on-install:6.1.2"}, + {name: "star-ref-ver", input: &packages.PackageVersionOverride{PackageReferenceName: "pterm-on-install", Version: "6.1.2"}, expect: "*:pterm-on-install:6.1.2"}, + {name: "pkg-action-ref-ver", input: &packages.PackageVersionOverride{PackageReferenceName: "pterm", PackageID: "pterm", ActionName: "Install", Version: "1.2.3"}, expect: "pterm:pterm:1.2.3"}, } for _, test := range tests { @@ -2404,25 +2405,25 @@ func TestReleaseCreate_ToPackageOverrideString(t *testing.T) { func TestReleaseCreate_ParsePackageOverrideString(t *testing.T) { tests := []struct { input string - expect *create.AmbiguousPackageVersionOverride + expect *packages.AmbiguousPackageVersionOverride expectErr error }{ - {input: ":5", expect: &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, - {input: "::5", expect: &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, - {input: "*:5", expect: &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, - {input: "*:*:5", expect: &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, - {input: ":*:5", expect: &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, - {input: "NuGet:NuGet:0.1", expect: &create.AmbiguousPackageVersionOverride{PackageReferenceName: "NuGet", ActionNameOrPackageID: "NuGet", Version: "0.1"}}, - {input: "NuGet:nuget-on-install:0.1", expect: &create.AmbiguousPackageVersionOverride{PackageReferenceName: "nuget-on-install", ActionNameOrPackageID: "NuGet", Version: "0.1"}}, - {input: "Install:nuget-on-install:0.1", expect: &create.AmbiguousPackageVersionOverride{PackageReferenceName: "nuget-on-install", ActionNameOrPackageID: "Install", Version: "0.1"}}, - {input: "pterm:9.7-pre-xyz", expect: &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "pterm", Version: "9.7-pre-xyz"}}, - {input: "pterm:55", expect: &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "pterm", Version: "55"}}, - {input: "pterm::55", expect: &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "pterm", Version: "55"}}, - {input: ":Push Package:55", expect: &create.AmbiguousPackageVersionOverride{PackageReferenceName: "Push Package", ActionNameOrPackageID: "", Version: "55"}}, - {input: "*:Push Package:55", expect: &create.AmbiguousPackageVersionOverride{PackageReferenceName: "Push Package", ActionNameOrPackageID: "", Version: "55"}}, - - {input: "pterm/Push Package=9.7-pre-xyz", expect: &create.AmbiguousPackageVersionOverride{PackageReferenceName: "Push Package", ActionNameOrPackageID: "pterm", Version: "9.7-pre-xyz"}}, - {input: "pterm=Push Package/9.7-pre-xyz", expect: &create.AmbiguousPackageVersionOverride{PackageReferenceName: "Push Package", ActionNameOrPackageID: "pterm", Version: "9.7-pre-xyz"}}, + {input: ":5", expect: &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, + {input: "::5", expect: &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, + {input: "*:5", expect: &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, + {input: "*:*:5", expect: &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, + {input: ":*:5", expect: &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "", Version: "5"}}, + {input: "NuGet:NuGet:0.1", expect: &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "NuGet", ActionNameOrPackageID: "NuGet", Version: "0.1"}}, + {input: "NuGet:nuget-on-install:0.1", expect: &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "nuget-on-install", ActionNameOrPackageID: "NuGet", Version: "0.1"}}, + {input: "Install:nuget-on-install:0.1", expect: &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "nuget-on-install", ActionNameOrPackageID: "Install", Version: "0.1"}}, + {input: "pterm:9.7-pre-xyz", expect: &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "pterm", Version: "9.7-pre-xyz"}}, + {input: "pterm:55", expect: &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "pterm", Version: "55"}}, + {input: "pterm::55", expect: &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "pterm", Version: "55"}}, + {input: ":Push Package:55", expect: &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "Push Package", ActionNameOrPackageID: "", Version: "55"}}, + {input: "*:Push Package:55", expect: &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "Push Package", ActionNameOrPackageID: "", Version: "55"}}, + + {input: "pterm/Push Package=9.7-pre-xyz", expect: &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "Push Package", ActionNameOrPackageID: "pterm", Version: "9.7-pre-xyz"}}, + {input: "pterm=Push Package/9.7-pre-xyz", expect: &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "Push Package", ActionNameOrPackageID: "pterm", Version: "9.7-pre-xyz"}}, {input: "", expectErr: errors.New("empty package version specification")}, @@ -2438,7 +2439,7 @@ func TestReleaseCreate_ParsePackageOverrideString(t *testing.T) { for _, test := range tests { t.Run(test.input, func(t *testing.T) { - result, err := create.ParsePackageOverrideString(test.input) + result, err := packages.ParsePackageOverrideString(test.input) assert.Equal(t, test.expectErr, err) assert.Equal(t, test.expect, result) }) @@ -2448,139 +2449,139 @@ func TestReleaseCreate_ParsePackageOverrideString(t *testing.T) { func TestReleaseCreate_ResolvePackageOverride(t *testing.T) { // this is the packageId:5.0 syntax t.Run("match on package ID", func(t *testing.T) { // this is probably the most common thing people will do - nugetPackage := &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "NuGet", Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "NuGet", Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "NuGet", ActionName: "", Version: "5.0", PackageReferenceName: ""}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "NuGet", ActionName: "", Version: "5.0", PackageReferenceName: ""}, r) }) // this is the stepName:5.0 syntax t.Run("match on step name", func(t *testing.T) { // this is probably the most common thing people will do - nugetPackage := &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "Install", Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "Install", Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "", ActionName: "Install", Version: "5.0", PackageReferenceName: ""}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "", ActionName: "Install", Version: "5.0", PackageReferenceName: ""}, r) }) // this is the packageRef:*:5.0 syntax t.Run("match on packageRef", func(t *testing.T) { - nugetPackage := &create.AmbiguousPackageVersionOverride{PackageReferenceName: "NuGet-B", Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "NuGet-B", Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet-A"}, {PackageID: "NuGet", ActionName: "Verify", Version: "0.1", PackageReferenceName: "NuGet-B"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet-B"}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet-B"}, r) }) // this is the *:5.0 or :5.0 or just 5.0 syntax t.Run("match on just version", func(t *testing.T) { // this is probably the most common thing people will do - nugetPackage := &create.AmbiguousPackageVersionOverride{Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: ""}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: ""}, r) }) t.Run("match on just version doesn't even need any packages to look at", func(t *testing.T) { // this is probably the most common thing people will do - nugetPackage := &create.AmbiguousPackageVersionOverride{Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{Version: "5.0"} - steps := make([]*create.StepPackageVersion, 0) // baseline + steps := make([]*packages.StepPackageVersion, 0) // baseline - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: ""}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: ""}, r) }) t.Run("match on action+packageRef before packageID", func(t *testing.T) { // this is probably the most common thing people will do - nugetPackage := &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "Verify", PackageReferenceName: "NuGet", Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "Verify", PackageReferenceName: "NuGet", Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet"}, {PackageID: "NuGet", ActionName: "Verify", Version: "0.1", PackageReferenceName: "NuGet"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "", ActionName: "Verify", Version: "5.0", PackageReferenceName: "NuGet"}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "", ActionName: "Verify", Version: "5.0", PackageReferenceName: "NuGet"}, r) }) t.Run("match on packageID+packageRef picks the first one where they are the same", func(t *testing.T) { - nugetPackage := &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "NuGet", PackageReferenceName: "NuGet", Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "NuGet", PackageReferenceName: "NuGet", Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet"}, {PackageID: "NuGet", ActionName: "Verify", Version: "0.1", PackageReferenceName: "NuGet"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "NuGet", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet"}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "NuGet", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet"}, r) }) t.Run("match on packageID+packageRef picks the correct one where they are different", func(t *testing.T) { - nugetPackage := &create.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "NuGet", PackageReferenceName: "NuGet-B", Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{ActionNameOrPackageID: "NuGet", PackageReferenceName: "NuGet-B", Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet-A"}, {PackageID: "NuGet", ActionName: "Verify", Version: "0.1", PackageReferenceName: "NuGet-B"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "NuGet", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet-B"}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "NuGet", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet-B"}, r) }) t.Run("match on packageRef wins over match on ActionName", func(t *testing.T) { // we shouldn't get in this situation, but just in case we do :shrug: - nugetPackage := &create.AmbiguousPackageVersionOverride{PackageReferenceName: "NuGet-B", ActionNameOrPackageID: "Cheese", Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "NuGet-B", ActionNameOrPackageID: "Cheese", Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet-A"}, {PackageID: "NuGet", ActionName: "Verify", Version: "0.1", PackageReferenceName: "NuGet-B"}, {PackageID: "OtherPackage", ActionName: "Cheese", Version: "0.1", PackageReferenceName: "Cheese"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet-B"}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet-B"}, r) }) t.Run("match on packageRef wins over match on PackageID", func(t *testing.T) { // we shouldn't get in this situation, but just in case we do :shrug: - nugetPackage := &create.AmbiguousPackageVersionOverride{PackageReferenceName: "NuGet-B", ActionNameOrPackageID: "Cheese", Version: "5.0"} + nugetPackage := &packages.AmbiguousPackageVersionOverride{PackageReferenceName: "NuGet-B", ActionNameOrPackageID: "Cheese", Version: "5.0"} - steps := []*create.StepPackageVersion{ // baseline + steps := []*packages.StepPackageVersion{ // baseline {PackageID: "NuGet", ActionName: "Install", Version: "0.1", PackageReferenceName: "NuGet-A"}, {PackageID: "NuGet", ActionName: "Verify", Version: "0.1", PackageReferenceName: "NuGet-B"}, {PackageID: "Cheese", ActionName: "OtherAction", Version: "0.1", PackageReferenceName: "OtherAction"}, } - r, err := create.ResolvePackageOverride(nugetPackage, steps) + r, err := packages.ResolvePackageOverride(nugetPackage, steps) assert.Nil(t, err) - assert.Equal(t, &create.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet-B"}, r) + assert.Equal(t, &packages.PackageVersionOverride{PackageID: "", ActionName: "", Version: "5.0", PackageReferenceName: "NuGet-B"}, r) }) } func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { - standardPackageSpec := []*create.StepPackageVersion{ + standardPackageSpec := []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", Version: "0.12", PackageReferenceName: "pterm-on-install"}, {PackageID: "pterm", ActionName: "Push", Version: "0.12", PackageReferenceName: "pterm-on-push"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", Version: "5.4", PackageReferenceName: "nuget-on-install"}, @@ -2588,11 +2589,11 @@ func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { } t.Run("apply wildcard override", func(t *testing.T) { - result := create.ApplyPackageOverrides(standardPackageSpec, []*create.PackageVersionOverride{ + result := packages.ApplyPackageOverrides(standardPackageSpec, []*packages.PackageVersionOverride{ {Version: "99"}, }) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", Version: "99", PackageReferenceName: "pterm-on-install"}, {PackageID: "pterm", ActionName: "Push", Version: "99", PackageReferenceName: "pterm-on-push"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", Version: "99", PackageReferenceName: "nuget-on-install"}, @@ -2601,11 +2602,11 @@ func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { }) t.Run("apply one override based on package ID", func(t *testing.T) { - result := create.ApplyPackageOverrides(standardPackageSpec, []*create.PackageVersionOverride{ + result := packages.ApplyPackageOverrides(standardPackageSpec, []*packages.PackageVersionOverride{ {PackageID: "pterm", Version: "99"}, }) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", Version: "99", PackageReferenceName: "pterm-on-install"}, {PackageID: "pterm", ActionName: "Push", Version: "99", PackageReferenceName: "pterm-on-push"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", Version: "5.4", PackageReferenceName: "nuget-on-install"}, @@ -2614,11 +2615,11 @@ func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { }) t.Run("apply one override based on step name", func(t *testing.T) { - result := create.ApplyPackageOverrides(standardPackageSpec, []*create.PackageVersionOverride{ + result := packages.ApplyPackageOverrides(standardPackageSpec, []*packages.PackageVersionOverride{ {ActionName: "Install", Version: "99"}, }) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", Version: "99", PackageReferenceName: "pterm-on-install"}, {PackageID: "pterm", ActionName: "Push", Version: "0.12", PackageReferenceName: "pterm-on-push"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", Version: "99", PackageReferenceName: "nuget-on-install"}, @@ -2627,11 +2628,11 @@ func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { }) t.Run("apply one override based on both package and step name", func(t *testing.T) { - result := create.ApplyPackageOverrides(standardPackageSpec, []*create.PackageVersionOverride{ + result := packages.ApplyPackageOverrides(standardPackageSpec, []*packages.PackageVersionOverride{ {PackageID: "pterm", ActionName: "Install", Version: "99"}, }) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", Version: "99", PackageReferenceName: "pterm-on-install"}, {PackageID: "pterm", ActionName: "Push", Version: "0.12", PackageReferenceName: "pterm-on-push"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", Version: "5.4", PackageReferenceName: "nuget-on-install"}, @@ -2640,13 +2641,13 @@ func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { }) t.Run("apply multiple overrides", func(t *testing.T) { - result := create.ApplyPackageOverrides(standardPackageSpec, []*create.PackageVersionOverride{ + result := packages.ApplyPackageOverrides(standardPackageSpec, []*packages.PackageVersionOverride{ {Version: "0.1"}, {PackageID: "pterm", Version: "2"}, {PackageID: "NuGet.CommandLine", ActionName: "Push", Version: "6"}, }) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", Version: "2", PackageReferenceName: "pterm-on-install"}, {PackageID: "pterm", ActionName: "Push", Version: "2", PackageReferenceName: "pterm-on-push"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", Version: "0.1", PackageReferenceName: "nuget-on-install"}, @@ -2655,13 +2656,13 @@ func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { }) t.Run("apply multiple overrides; order matters", func(t *testing.T) { - result := create.ApplyPackageOverrides(standardPackageSpec, []*create.PackageVersionOverride{ + result := packages.ApplyPackageOverrides(standardPackageSpec, []*packages.PackageVersionOverride{ {PackageID: "pterm", Version: "2"}, {PackageID: "NuGet.CommandLine", ActionName: "Push", Version: "6"}, {Version: "0.1"}, // overwrites everything }) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", Version: "0.1", PackageReferenceName: "pterm-on-install"}, {PackageID: "pterm", ActionName: "Push", Version: "0.1", PackageReferenceName: "pterm-on-push"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", Version: "0.1", PackageReferenceName: "nuget-on-install"}, @@ -2670,18 +2671,18 @@ func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { }) t.Run("apply single override targeting only package-ref", func(t *testing.T) { - packageSpec := []*create.StepPackageVersion{ + packageSpec := []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", PackageReferenceName: "pterm-on-install", Version: "0.12"}, {PackageID: "pterm", ActionName: "Push", PackageReferenceName: "pterm", Version: "0.12"}, {PackageID: "pterm", ActionName: "Verify", PackageReferenceName: "pterm", Version: "0.12"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", PackageReferenceName: "NuGet.CommandLine", Version: "5.4"}, } - result := create.ApplyPackageOverrides(packageSpec, []*create.PackageVersionOverride{ + result := packages.ApplyPackageOverrides(packageSpec, []*packages.PackageVersionOverride{ {PackageReferenceName: "pterm", Version: "2000"}, }) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", PackageReferenceName: "pterm-on-install", Version: "0.12"}, {PackageID: "pterm", ActionName: "Push", PackageReferenceName: "pterm", Version: "2000"}, {PackageID: "pterm", ActionName: "Verify", PackageReferenceName: "pterm", Version: "2000"}, @@ -2691,18 +2692,18 @@ func TestReleaseCreate_ApplyPackageOverride(t *testing.T) { t.Run("target both of package-ref:action where package referencename matches another package too", func(t *testing.T) { // real bug observed with manual testing - packageSpec := []*create.StepPackageVersion{ + packageSpec := []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", PackageReferenceName: "pterm-on-install", Version: "0.12"}, {PackageID: "pterm", ActionName: "Push", PackageReferenceName: "pterm", Version: "0.12"}, {PackageID: "pterm", ActionName: "Verify", PackageReferenceName: "pterm", Version: "0.12"}, {PackageID: "NuGet.CommandLine", ActionName: "Install", PackageReferenceName: "NuGet.CommandLine", Version: "5.4"}, } - result := create.ApplyPackageOverrides(packageSpec, []*create.PackageVersionOverride{ + result := packages.ApplyPackageOverrides(packageSpec, []*packages.PackageVersionOverride{ {PackageReferenceName: "pterm-on-install", ActionName: "Install", Version: "20000"}, }) - assert.Equal(t, []*create.StepPackageVersion{ + assert.Equal(t, []*packages.StepPackageVersion{ {PackageID: "pterm", ActionName: "Install", PackageReferenceName: "pterm-on-install", Version: "20000"}, // only this one should be overridden {PackageID: "pterm", ActionName: "Push", PackageReferenceName: "pterm", Version: "0.12"}, {PackageID: "pterm", ActionName: "Verify", PackageReferenceName: "pterm", Version: "0.12"}, diff --git a/pkg/cmd/release/create/string_utilities.go b/pkg/cmd/release/create/string_utilities.go deleted file mode 100644 index e723d9c4..00000000 --- a/pkg/cmd/release/create/string_utilities.go +++ /dev/null @@ -1,43 +0,0 @@ -package create - -import "golang.org/x/exp/slices" - -// splitString splits the input string into components based on delimiter characters. -// we want to pick up empty entries here; so "::5" and ":pterm:5" should both return THREE components, rather than one or two -// and we want to allow for multiple different delimeters. -// neither the builtin golang strings.Split or strings.FieldsFunc support this. Logic borrowed from strings.FieldsFunc with heavy modifications -func splitString(s string, delimiters []rune) []string { - // pass 1: collect spans; golang strings.FieldsFunc says it's much more efficient this way - type span struct { - start int - end int - } - spans := make([]span, 0, 3) - - // Find the field start and end indices. - start := 0 // we always start the first span at the beginning of the string - for idx, ch := range s { - if slices.Contains(delimiters, ch) { - if start >= 0 { // we found a delimiter and we are already in a span; end the span and start a new one - spans = append(spans, span{start, idx}) - start = idx + 1 - } else { // we found a delimiter and we are not in a span; start a new span - if start < 0 { - start = idx - } - } - } - } - - // Last field might end at EOF. - if start >= 0 { - spans = append(spans, span{start, len(s)}) - } - - // pass 2: create strings from recorded field indices. - a := make([]string, len(spans)) - for i, span := range spans { - a[i] = s[span.start:span.end] - } - return a -} diff --git a/pkg/cmd/release/create/gitResources.go b/pkg/gitresources/gitResources.go similarity index 96% rename from pkg/cmd/release/create/gitResources.go rename to pkg/gitresources/gitResources.go index 15ecb79e..98c643f7 100644 --- a/pkg/cmd/release/create/gitResources.go +++ b/pkg/gitresources/gitResources.go @@ -1,16 +1,18 @@ -package create +package gitresources import ( "errors" "fmt" + "io" + "strings" + "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc/v2" "github.com/OctopusDeploy/cli/pkg/output" "github.com/OctopusDeploy/cli/pkg/question" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/deployments" + "github.com/OctopusDeploy/cli/pkg/util" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/releases" "golang.org/x/exp/slices" - "io" - "strings" ) var gitResourceGitRefLoopHelpText = heredoc.Doc(` @@ -41,10 +43,10 @@ bold(GIT RESOURCE OVERRIDE STRINGS) dim(---------------------------------------------------------------------) `) // note this expects to have prettifyHelp run over it -func BuildGitResourcesBaseline(deploymentProcessTemplate *deployments.DeploymentProcessTemplate) []*GitResourceGitRef { - result := make([]*GitResourceGitRef, 0, len(deploymentProcessTemplate.GitResources)) +func BuildGitResourcesBaseline(gitResources []releases.ReleaseTemplateGitResource) []*GitResourceGitRef { + result := make([]*GitResourceGitRef, 0, len(gitResources)) - for _, gitResource := range deploymentProcessTemplate.GitResources { + for _, gitResource := range gitResources { result = append(result, &GitResourceGitRef{ ActionName: gitResource.ActionName, GitRef: gitResource.DefaultBranch, @@ -187,7 +189,7 @@ func ParseGitResourceGitRefString(gitResourceRef string) (*GitResourceGitRef, er } //for consistency with the server-side code, we only support one type of delimiter at once - components := splitString(gitResourceRef, []rune{delimiters[chosenDelimiterIndex]}) + components := util.SplitString(gitResourceRef, []rune{delimiters[chosenDelimiterIndex]}) actionName, gitResourceName, gitRef := "", "", "" // We support 2 formats of git resource diff --git a/pkg/cmd/release/create/gitResources_test.go b/pkg/gitresources/gitResources_test.go similarity index 50% rename from pkg/cmd/release/create/gitResources_test.go rename to pkg/gitresources/gitResources_test.go index 07d788a8..7dd43cd6 100644 --- a/pkg/cmd/release/create/gitResources_test.go +++ b/pkg/gitresources/gitResources_test.go @@ -1,32 +1,33 @@ -package create_test +package gitresources_test import ( "bytes" "errors" + "testing" + "github.com/AlecAivazis/survey/v2" - "github.com/OctopusDeploy/cli/pkg/cmd/release/create" + "github.com/OctopusDeploy/cli/pkg/gitresources" "github.com/OctopusDeploy/cli/test/testutil" "github.com/stretchr/testify/assert" - "testing" ) func TestReleaseCreate_ParseGitResourceOverrideString(t *testing.T) { tests := []struct { input string - expect *create.GitResourceGitRef + expect *gitresources.GitResourceGitRef isErrorExpected bool }{ //Valid inputs - {input: "Action:main", expect: &create.GitResourceGitRef{ActionName: "Action", GitRef: "main", GitResourceName: ""}}, - {input: "Action:*", expect: &create.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: ""}}, - {input: "Action=refs/heads/main", expect: &create.GitResourceGitRef{ActionName: "Action", GitRef: "refs/heads/main", GitResourceName: ""}}, - {input: "Action=*", expect: &create.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: ""}}, - {input: "Action:Name1:main", expect: &create.GitResourceGitRef{ActionName: "Action", GitRef: "main", GitResourceName: "Name1"}}, - {input: "Action:Name1:*", expect: &create.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: "Name1"}}, - {input: "Action=Name1=refs/heads/main", expect: &create.GitResourceGitRef{ActionName: "Action", GitRef: "refs/heads/main", GitResourceName: "Name1"}}, - {input: "Action=Name1=*", expect: &create.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: "Name1"}}, + {input: "Action:main", expect: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "main", GitResourceName: ""}}, + {input: "Action:*", expect: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: ""}}, + {input: "Action=refs/heads/main", expect: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "refs/heads/main", GitResourceName: ""}}, + {input: "Action=*", expect: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: ""}}, + {input: "Action:Name1:main", expect: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "main", GitResourceName: "Name1"}}, + {input: "Action:Name1:*", expect: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: "Name1"}}, + {input: "Action=Name1=refs/heads/main", expect: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "refs/heads/main", GitResourceName: "Name1"}}, + {input: "Action=Name1=*", expect: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: "Name1"}}, //Mixing delimiters is NOT supported (consistent with server-side) this results in an Action name that contains an = (as : is the high preference delimiter) - {input: "Action=Name1:*", expect: &create.GitResourceGitRef{ActionName: "Action=Name1", GitRef: "*", GitResourceName: ""}}, + {input: "Action=Name1:*", expect: &gitresources.GitResourceGitRef{ActionName: "Action=Name1", GitRef: "*", GitResourceName: ""}}, //Invalid inputs {input: "", isErrorExpected: true}, @@ -38,7 +39,7 @@ func TestReleaseCreate_ParseGitResourceOverrideString(t *testing.T) { for _, test := range tests { t.Run(test.input, func(t *testing.T) { - result, err := create.ParseGitResourceGitRefString(test.input) + result, err := gitresources.ParseGitResourceGitRefString(test.input) assert.Equal(t, test.isErrorExpected, err != nil) assert.Equal(t, test.expect, result) }) @@ -48,13 +49,13 @@ func TestReleaseCreate_ParseGitResourceOverrideString(t *testing.T) { func TestReleaseCreate_ToGitResourceGitRefString(t *testing.T) { tests := []struct { name string - input *create.GitResourceGitRef + input *gitresources.GitResourceGitRef expect string }{ - {name: "primary git resource", input: &create.GitResourceGitRef{ActionName: "Action", GitRef: "refs/heads/main", GitResourceName: ""}, expect: "Action:refs/heads/main"}, - {name: "primary git resource with wildcard", input: &create.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: ""}, expect: "Action:*"}, - {name: "secondary git resource", input: &create.GitResourceGitRef{ActionName: "Action", GitRef: "refs/heads/main", GitResourceName: "Name1"}, expect: "Action:Name1:refs/heads/main"}, - {name: "secondary git resource with wildcard", input: &create.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: "Name1"}, expect: "Action:Name1:*"}, + {name: "primary git resource", input: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "refs/heads/main", GitResourceName: ""}, expect: "Action:refs/heads/main"}, + {name: "primary git resource with wildcard", input: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: ""}, expect: "Action:*"}, + {name: "secondary git resource", input: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "refs/heads/main", GitResourceName: "Name1"}, expect: "Action:Name1:refs/heads/main"}, + {name: "secondary git resource with wildcard", input: &gitresources.GitResourceGitRef{ActionName: "Action", GitRef: "*", GitResourceName: "Name1"}, expect: "Action:Name1:*"}, } for _, test := range tests { @@ -67,7 +68,7 @@ func TestReleaseCreate_ToGitResourceGitRefString(t *testing.T) { } func TestReleaseCreate_ResolveGitResourceOverride(t *testing.T) { - baseline := []*create.GitResourceGitRef{ + baseline := []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/main", GitResourceName: ""}, {ActionName: "Action1", GitRef: "refs/tags/test", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "release/v1", GitResourceName: ""}, @@ -76,45 +77,45 @@ func TestReleaseCreate_ResolveGitResourceOverride(t *testing.T) { tests := []struct { name string - input *create.GitResourceGitRef - expect *create.GitResourceGitRef + input *gitresources.GitResourceGitRef + expect *gitresources.GitResourceGitRef isErrorExpected bool }{ //matching cases {name: "matches primary git resource", - input: &create.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: ""}, - expect: &create.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: ""}}, + input: &gitresources.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: ""}, + expect: &gitresources.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: ""}}, {name: "matches secondary git resource", - input: &create.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: "Name1"}, - expect: &create.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: "Name1"}}, + input: &gitresources.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: "Name1"}, + expect: &gitresources.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: "Name1"}}, {name: "matches primary git resource with wildcard", - input: &create.GitResourceGitRef{ActionName: "Action1", GitRef: "*", GitResourceName: ""}, - expect: &create.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/main", GitResourceName: ""}}, + input: &gitresources.GitResourceGitRef{ActionName: "Action1", GitRef: "*", GitResourceName: ""}, + expect: &gitresources.GitResourceGitRef{ActionName: "Action1", GitRef: "refs/heads/main", GitResourceName: ""}}, {name: "matches secondary git resource with wildcard", - input: &create.GitResourceGitRef{ActionName: "Action3", GitRef: "*", GitResourceName: "Name1"}, - expect: &create.GitResourceGitRef{ActionName: "Action3", GitRef: "refs/tags/v1", GitResourceName: "Name1"}}, + input: &gitresources.GitResourceGitRef{ActionName: "Action3", GitRef: "*", GitResourceName: "Name1"}, + expect: &gitresources.GitResourceGitRef{ActionName: "Action3", GitRef: "refs/tags/v1", GitResourceName: "Name1"}}, //non-matching cases {name: "does not match secondary git resource by action name", - input: &create.GitResourceGitRef{ActionName: "Action2", GitRef: "*", GitResourceName: "Name1"}, + input: &gitresources.GitResourceGitRef{ActionName: "Action2", GitRef: "*", GitResourceName: "Name1"}, isErrorExpected: true, }, {name: "does not match secondary git resource by git resource name", - input: &create.GitResourceGitRef{ActionName: "Action1", GitRef: "*", GitResourceName: "Name2"}, + input: &gitresources.GitResourceGitRef{ActionName: "Action1", GitRef: "*", GitResourceName: "Name2"}, isErrorExpected: true, }, {name: "does not match primary git resource", - input: &create.GitResourceGitRef{ActionName: "Action3", GitRef: "*", GitResourceName: ""}, + input: &gitresources.GitResourceGitRef{ActionName: "Action3", GitRef: "*", GitResourceName: ""}, isErrorExpected: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - result, err := create.ResolveGitResourceOverride(test.input, baseline) + result, err := gitresources.ResolveGitResourceOverride(test.input, baseline) assert.Equal(t, test.isErrorExpected, err != nil) assert.Equal(t, test.expect, result) @@ -125,7 +126,7 @@ func TestReleaseCreate_ResolveGitResourceOverride(t *testing.T) { } func TestReleaseCreate_ApplyGitResourceOverrides(t *testing.T) { - baseline := []*create.GitResourceGitRef{ + baseline := []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/main", GitResourceName: ""}, {ActionName: "Action1", GitRef: "refs/tags/test", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "release/v1", GitResourceName: ""}, @@ -133,11 +134,11 @@ func TestReleaseCreate_ApplyGitResourceOverrides(t *testing.T) { } t.Run("no overrides results in new copied objects", func(t *testing.T) { - var overrides []*create.GitResourceGitRef + var overrides []*gitresources.GitResourceGitRef - result := create.ApplyGitResourceOverrides(baseline, overrides) + result := gitresources.ApplyGitResourceOverrides(baseline, overrides) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/main", GitResourceName: ""}, {ActionName: "Action1", GitRef: "refs/tags/test", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "release/v1", GitResourceName: ""}, @@ -151,14 +152,14 @@ func TestReleaseCreate_ApplyGitResourceOverrides(t *testing.T) { }) t.Run("applies specified overrides", func(t *testing.T) { - overrides := []*create.GitResourceGitRef{ + overrides := []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/tags/1.0.0", GitResourceName: ""}, {ActionName: "Action3", GitRef: "refs/tags/v1.0.0", GitResourceName: "Name1"}, } - result := create.ApplyGitResourceOverrides(baseline, overrides) + result := gitresources.ApplyGitResourceOverrides(baseline, overrides) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/tags/1.0.0", GitResourceName: ""}, {ActionName: "Action1", GitRef: "refs/tags/test", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "release/v1", GitResourceName: ""}, @@ -167,13 +168,13 @@ func TestReleaseCreate_ApplyGitResourceOverrides(t *testing.T) { }) t.Run("applies specified overrides with wildcards", func(t *testing.T) { - overrides := []*create.GitResourceGitRef{ + overrides := []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "*", GitResourceName: ""}, } - result := create.ApplyGitResourceOverrides(baseline, overrides) + result := gitresources.ApplyGitResourceOverrides(baseline, overrides) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/main", GitResourceName: ""}, {ActionName: "Action1", GitRef: "refs/tags/test", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "release/v1", GitResourceName: ""}, @@ -182,14 +183,14 @@ func TestReleaseCreate_ApplyGitResourceOverrides(t *testing.T) { }) t.Run("only applies matching overrides", func(t *testing.T) { - overrides := []*create.GitResourceGitRef{ + overrides := []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/tags/1.0.0", GitResourceName: ""}, {ActionName: "Action2", GitRef: "*", GitResourceName: "Name1"}, } - result := create.ApplyGitResourceOverrides(baseline, overrides) + result := gitresources.ApplyGitResourceOverrides(baseline, overrides) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/tags/1.0.0", GitResourceName: ""}, {ActionName: "Action1", GitRef: "refs/tags/test", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "release/v1", GitResourceName: ""}, @@ -199,7 +200,7 @@ func TestReleaseCreate_ApplyGitResourceOverrides(t *testing.T) { } func TestReleaseCreate_AskQuestions_AskGitResourceOverrideLoop(t *testing.T) { - baseline := []*create.GitResourceGitRef{ + baseline := []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/main", GitResourceName: ""}, {ActionName: "Action1", GitRef: "refs/tags/test", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "release/v1", GitResourceName: ""}, @@ -212,64 +213,64 @@ func TestReleaseCreate_AskQuestions_AskGitResourceOverrideLoop(t *testing.T) { }{ // this is the happy path where the CLI presents the list of server-selected git resources and they just go 'yep' {"no-op test", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) //nothing was overridden, so an empty array - assert.Equal(t, make([]*create.GitResourceGitRef, 0), overrides) + assert.Equal(t, make([]*gitresources.GitResourceGitRef, 0), overrides) }}, {"override primary git resource", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action1:refs/heads/elephant") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action1:refs/heads/elephant") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: ""}, }, overrides) }}, {"override secondary git resource", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action1:Name1:refs/heads/elephant") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action1:Name1:refs/heads/elephant") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: "Name1"}, }, overrides) }}, {"multiple overrides of same git resource results in last override used", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action1:refs/heads/elephant") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action1:refs/heads/dinosaur") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action1:refs/heads/lion") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action1:refs/heads/elephant") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action1:refs/heads/dinosaur") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action1:refs/heads/lion") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/lion", GitResourceName: ""}, }, overrides) }}, @@ -277,45 +278,45 @@ func TestReleaseCreate_AskQuestions_AskGitResourceOverrideLoop(t *testing.T) { {"entering the loop with --git-resource picked up from the command line", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { cmdlineGitResources := []string{"Action1:Name1:refs/tags/1.0.0", "Action2:refs/heads/abc123"} - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, cmdlineGitResources, qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, cmdlineGitResources, qa.AsAsker(), stdout) }) - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/tags/1.0.0", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "refs/heads/abc123", GitResourceName: ""}, }, overrides) }}, {"blank answer retries the question", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - validationErr := qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("") + validationErr := qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("") assert.Nil(t, validationErr) - validationErr = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("") + validationErr = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("") assert.Nil(t, validationErr) - validationErr = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") + validationErr = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") assert.Nil(t, validationErr) overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, make([]*create.GitResourceGitRef, 0), overrides) + assert.Equal(t, make([]*gitresources.GitResourceGitRef, 0), overrides) }}, {"can't specify garbage; question loop retries", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - q := qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}) + q := qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}) validationErr := q.AnswerWith("fish") // not enough components assert.EqualError(t, validationErr, "git resource git reference specification \"fish\" does not use an expected format") @@ -330,21 +331,21 @@ func TestReleaseCreate_AskQuestions_AskGitResourceOverrideLoop(t *testing.T) { assert.Nil(t, validationErr) // it'll ask again; y to confirm - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") // confirm packages + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") // confirm packages overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: ""}, }, overrides) }}, {"can't specify git resources or steps that aren't there due to validator; question loop retries", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - q := qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}) + q := qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}) validationErr := q.AnswerWith("banana:refs/heads/main") assert.EqualError(t, validationErr, "could not resolve step name \"banana\" or git resource name \"\"") @@ -356,23 +357,23 @@ func TestReleaseCreate_AskQuestions_AskGitResourceOverrideLoop(t *testing.T) { assert.Nil(t, validationErr) // it'll ask again; y to confirm - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") // confirm packages + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") // confirm packages overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/heads/elephant", GitResourceName: ""}, }, overrides) }}, {"question loop doesn't retry if it gets a hard error", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) expectedErr := errors.New("hard fail") - qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWithError(expectedErr) + qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWithError(expectedErr) overrides, err := testutil.ReceivePair(receiver) assert.Equal(t, expectedErr, err) @@ -380,46 +381,46 @@ func TestReleaseCreate_AskQuestions_AskGitResourceOverrideLoop(t *testing.T) { }}, {"multiple overrides with undo", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action1:Name1:refs/tags/1.0.0") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action1:Name1:refs/tags/1.0.0") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action2:refs/tags/1.0.0") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action2:refs/tags/1.0.0") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("u") // undo Action2:refs/tags/1.0.0 + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("u") // undo Action2:refs/tags/1.0.0 - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action2:refs/heads/abc123") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action2:refs/heads/abc123") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action1", GitRef: "refs/tags/1.0.0", GitResourceName: "Name1"}, {ActionName: "Action2", GitRef: "refs/heads/abc123", GitResourceName: ""}, }, overrides) }}, {"multiple overrides with reset", func(t *testing.T, qa *testutil.AskMocker, stdout *bytes.Buffer) { - receiver := testutil.GoBegin2(func() ([]*create.GitResourceGitRef, error) { - return create.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) + receiver := testutil.GoBegin2(func() ([]*gitresources.GitResourceGitRef, error) { + return gitresources.AskGitResourceOverrideLoop(baseline, make([]string, 0), qa.AsAsker(), stdout) }) - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action1:Name1:refs/tags/1.0.0") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action1:Name1:refs/tags/1.0.0") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action2:refs/tags/1.0.0") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action2:refs/tags/1.0.0") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("r") // undo Action1:Name1:refs/tags/1.0.0 and Action2:refs/tags/1.0.0 + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("r") // undo Action1:Name1:refs/tags/1.0.0 and Action2:refs/tags/1.0.0 - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("Action3:Name1:refs/heads/abc123") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("Action3:Name1:refs/heads/abc123") - _ = qa.ExpectQuestion(t, &survey.Input{Message: create.GitResourceOverrideQuestion}).AnswerWith("y") + _ = qa.ExpectQuestion(t, &survey.Input{Message: gitresources.GitResourceOverrideQuestion}).AnswerWith("y") overrides, err := testutil.ReceivePair(receiver) assert.Nil(t, err) - assert.Equal(t, []*create.GitResourceGitRef{ + assert.Equal(t, []*gitresources.GitResourceGitRef{ {ActionName: "Action3", GitRef: "refs/heads/abc123", GitResourceName: "Name1"}, }, overrides) }}, diff --git a/pkg/packages/packages.go b/pkg/packages/packages.go new file mode 100644 index 00000000..8a230452 --- /dev/null +++ b/pkg/packages/packages.go @@ -0,0 +1,605 @@ +package packages + +import ( + "errors" + "fmt" + "io" + "regexp" + "sort" + "strings" + + "github.com/AlecAivazis/survey/v2" + "github.com/MakeNowJust/heredoc/v2" + "github.com/OctopusDeploy/cli/pkg/output" + "github.com/OctopusDeploy/cli/pkg/question" + "github.com/OctopusDeploy/cli/pkg/util" + octopusApiClient "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/releases" +) + +var packageOverrideLoopHelpText = heredoc.Doc(` +bold(PACKAGE SELECTION) + This screen presents the list of packages used by your project, and the steps + which reference them. + If an item is dimmed (gray text) this indicates that the attribute is duplicated. + For example if you reference the same package in two steps, the second will be dimmed. + +bold(COMMANDS) + Any any point, you can enter one of the following: + - green(?) to access this help screen + - green(y) to accept the list of packages and proceed with creating the release + - green(u) to undo the last edit you made to package versions + - green(r) to reset all package version edits + - A package override string. + +bold(PACKAGE OVERRIDE STRINGS) + Package override strings must have 2 or 3 components, separated by a : + The last component must always be a version number. + + When specifying 2 components, the first component is either a Package ID or a Step Name. + You can also specify a * which will match all packages + Examples: + bold(octopustools:9.1) dim(# sets package 'octopustools' in all steps to v 9.1) + bold(Push Package:3.0) dim(# sets all packages in the 'Push Package' step to v 3.0) + bold(*:5.1) dim(# sets all packages in all steps to v 5.1) + + The 3-component syntax is for advanced use cases where you reference the same package twice + in a single step, and need to distinguish between the two. + The syntax is bold(packageIDorStepName:packageReferenceName:version) + Please refer to the octopus server documentation for more information regarding package reference names. + +dim(---------------------------------------------------------------------) +`) // note this expects to have prettifyHelp run over it + +type StepPackageVersion struct { + // these 3 fields are the main ones for showing the user + PackageID string + ActionName string // "StepName is an obsolete alias for ActionName, they always contain the same value" + Version string // note this may be an empty string, indicating that no version could be found for this package yet + + // used to locate the deployment process VersioningStrategy Donor Package + PackageReferenceName string +} + +// BuildPackageVersionBaseline loads the deployment process template from the server, and for each step+package therein, +// finds the latest available version satisfying the channel version rules. Result is the list of step+package+versions +// to use as a baseline. The package version override process takes this as an input and layers on top of it +func BuildPackageVersionBaseline(octopus *octopusApiClient.Client, packages []releases.ReleaseTemplatePackage, setAdditionalFeedQueryParameters func(releases.ReleaseTemplatePackage, feeds.SearchPackageVersionsQuery)) ([]*StepPackageVersion, error) { + result := make([]*StepPackageVersion, 0, len(packages)) + + // step 1: pass over all the packages in the deployment process, group them + // by their feed, then subgroup by packageId + + // map(key: FeedID, value: list of references using the package so we can trace back to steps) + feedsToQuery := make(map[string][]releases.ReleaseTemplatePackage) + for _, pkg := range packages { + + if pkg.FixedVersion != "" { + // If a package has a fixed version it shouldn't be displayed or overridable at all + continue + } + + // If a package is not considered resolvable by the server, don't attempt to query it's feed or lookup + // any potential versions for it; we can't succeed in that because variable templates won't get expanded + // until deployment time + if !pkg.IsResolvable { + result = append(result, &StepPackageVersion{ + PackageID: pkg.PackageID, + ActionName: pkg.ActionName, + PackageReferenceName: pkg.PackageReferenceName, + Version: "", + }) + continue + } + if feedPackages, seenFeedBefore := feedsToQuery[pkg.FeedID]; !seenFeedBefore { + feedsToQuery[pkg.FeedID] = []releases.ReleaseTemplatePackage{pkg} + } else { + // seen both the feed and package, but not against this particular step + feedsToQuery[pkg.FeedID] = append(feedPackages, pkg) + } + } + + if len(feedsToQuery) == 0 { + return make([]*StepPackageVersion, 0), nil + } + + // step 2: load the feed resources, so we can get SearchPackageVersionsTemplate + feedIds := make([]string, 0, len(feedsToQuery)) + for k := range feedsToQuery { + feedIds = append(feedIds, k) + } + sort.Strings(feedIds) // we need to sort them otherwise the order is indeterminate. Server doesn't care but our unit tests fail + foundFeeds, err := octopus.Feeds.Get(feeds.FeedsQuery{IDs: feedIds, Take: len(feedIds)}) + if err != nil { + return nil, err + } + + // step 3: for each package within a feed, ask the server to select the best package version for it, applying the channel rules + for _, feed := range foundFeeds.Items { + packageRefsInFeed, ok := feedsToQuery[feed.GetID()] + if !ok { + return nil, errors.New("internal consistency error; feed ID not found in feedsToQuery") // should never happen + } + + cache := make(map[feeds.SearchPackageVersionsQuery]string) // cache value is the package version + + for _, packageRef := range packageRefsInFeed { + query := feeds.SearchPackageVersionsQuery{ + PackageID: packageRef.PackageID, + Take: 1, + } + setAdditionalFeedQueryParameters(packageRef, query) + + if cachedVersion, ok := cache[query]; ok { + result = append(result, &StepPackageVersion{ + PackageID: packageRef.PackageID, + ActionName: packageRef.ActionName, + PackageReferenceName: packageRef.PackageReferenceName, + Version: cachedVersion, + }) + } else { // uncached; ask the server + versions, err := octopus.Feeds.SearchFeedPackageVersions(feed, query) + if err != nil { + return nil, err + } + + switch len(versions.Items) { + case 0: // no package found; cache the response + cache[query] = "" + result = append(result, &StepPackageVersion{ + PackageID: packageRef.PackageID, + ActionName: packageRef.ActionName, + PackageReferenceName: packageRef.PackageReferenceName, + Version: "", + }) + + case 1: + cache[query] = versions.Items[0].Version + result = append(result, &StepPackageVersion{ + PackageID: packageRef.PackageID, + ActionName: packageRef.ActionName, + PackageReferenceName: packageRef.PackageReferenceName, + Version: versions.Items[0].Version, + }) + + default: + return nil, errors.New("internal error; more than one package returned when only 1 specified") + } + } + } + } + return result, nil +} + +type PackageVersionOverride struct { + ActionName string // optional, but one or both of ActionName or PackageID must be supplied + PackageID string // optional, but one or both of ActionName or PackageID must be supplied + PackageReferenceName string // optional; use for advanced situations where the same package is referenced multiple times by a single step + Version string // required +} + +// ToPackageOverrideString converts the struct back into a string which the server can parse e.g. StepName:Version. +// This is the inverse of ParsePackageOverrideString +func (p *PackageVersionOverride) ToPackageOverrideString() string { + components := make([]string, 0, 3) + + // stepNameOrPackageID always comes first if we have it + if p.PackageID != "" { + components = append(components, p.PackageID) + } else if p.ActionName != "" { // can't have both PackageID and ActionName; PackageID wins + components = append(components, p.ActionName) + } + + // followed by package reference name if we have it + if p.PackageReferenceName != "" { + if len(components) == 0 { // if we have an explicit packagereference but no packageId or action, we need to express it with *:ref:version + components = append(components, "*") + } + components = append(components, p.PackageReferenceName) + } + + if len(components) == 0 { // the server can't deal with just a number by itself; if we want to override everything we must pass *:Version + components = append(components, "*") + } + components = append(components, p.Version) + + return strings.Join(components, ":") +} + +// splitPackageOverrideString splits the input string into components based on delimiter characters. +// we want to pick up empty entries here; so "::5" and ":pterm:5" should both return THREE components, rather than one or two +// and we want to allow for multiple different delimeters. +// neither the builtin golang strings.Split or strings.FieldsFunc support this. Logic borrowed from strings.FieldsFunc with heavy modifications +func splitPackageOverrideString(s string) []string { + return util.SplitString(s, []int32{':', '/', '='}) +} + +// AmbiguousPackageVersionOverride tells us that we want to set the version of some package to `Version` +// but it's not clear whether ActionNameOrPackageID refers to an ActionName or PackageID at this point +type AmbiguousPackageVersionOverride struct { + ActionNameOrPackageID string + PackageReferenceName string + Version string +} + +// taken from here https://github.com/OctopusDeploy/Versioning/blob/main/source/Octopus.Versioning/Octopus/OctopusVersionParser.cs#L29 +// but simplified, and removed the support for optional whitespace around version numbers (OctopusVersion would allow "1 . 2 . 3" whereas we won't +// otherwise this is very lenient +var validVersionRegex, _ = regexp.Compile("(?i)" + `^\s*(v|V)?\d+(\.\d+)?(\.\d+)?(\.\d+)?[.\-_\\]?([a-z0-9]*?)([.\-_\\]([a-z0-9.\-_\\]*?)?)?(\+([a-z0-9_\-.\\+]*?))?$`) + +func isValidVersion(version string) bool { + return validVersionRegex.MatchString(version) +} + +// ParsePackageOverrideString parses a package version override string into a structure. +// Logic should align with PackageVersionResolver in the Octopus Server and .NET CLI +// In cases where things are ambiguous, we look in steps for matching values to see if something is a PackageID or a StepName +func ParsePackageOverrideString(packageOverride string) (*AmbiguousPackageVersionOverride, error) { + if packageOverride == "" { + return nil, errors.New("empty package version specification") + } + + components := splitPackageOverrideString(packageOverride) + packageReferenceName, stepNameOrPackageID, version := "", "", "" + + switch len(components) { + case 2: + // if there are two components it is (StepName|PackageID):Version + stepNameOrPackageID, version = strings.TrimSpace(components[0]), strings.TrimSpace(components[1]) + case 3: + // if there are three components it is (StepName|PackageID):PackageReferenceName:Version + stepNameOrPackageID, packageReferenceName, version = strings.TrimSpace(components[0]), strings.TrimSpace(components[1]), strings.TrimSpace(components[2]) + default: + return nil, fmt.Errorf("package version specification \"%s\" does not use expected format", packageOverride) + } + + // must always specify a version; must specify either packageID, stepName or both + if version == "" { + return nil, fmt.Errorf("package version specification \"%s\" does not use expected format", packageOverride) + } + if !isValidVersion(version) { + return nil, fmt.Errorf("version component \"%s\" is not a valid version", version) + } + + // compensate for wildcards + if packageReferenceName == "*" { + packageReferenceName = "" + } + if stepNameOrPackageID == "*" { + stepNameOrPackageID = "" + } + + return &AmbiguousPackageVersionOverride{ + ActionNameOrPackageID: stepNameOrPackageID, + PackageReferenceName: packageReferenceName, + Version: version, + }, nil +} + +func ResolvePackageOverride(override *AmbiguousPackageVersionOverride, steps []*StepPackageVersion) (*PackageVersionOverride, error) { + // shortcut for wildcard matches; these match everything so we don't need to do any work + if override.PackageReferenceName == "" && override.ActionNameOrPackageID == "" { + return &PackageVersionOverride{ + ActionName: "", + PackageID: "", + PackageReferenceName: "", + Version: override.Version, + }, nil + } + + actionNameOrPackageID := override.ActionNameOrPackageID + + // it could be either a stepname or a package ID; match against the list of packages to try and guess. + // logic matching the server: + // - exact match on stepName + refName + // - then exact match on packageId + refName + // - then match on * + refName + // - then match on stepName + * + // - then match on packageID + * + type match struct { + priority int + actionName string // if set we matched on actionName, else we didn't + packageID string // if set we matched on packageID, else we didn't + packageReferenceName string // if set we matched on packageReferenceName, else we didn't + } + + matches := make([]match, 0, 2) // common case is likely to be 2; if we have a packageID then we may match both exactly and partially on the ID depending on referenceName + for _, p := range steps { + if p.ActionName != "" && p.ActionName == actionNameOrPackageID { + if p.PackageReferenceName == override.PackageReferenceName { + matches = append(matches, match{priority: 100, actionName: p.ActionName, packageReferenceName: p.PackageReferenceName}) + } else { + matches = append(matches, match{priority: 50, actionName: p.ActionName}) + } + } else if p.PackageID != "" && p.PackageID == actionNameOrPackageID { + if p.PackageReferenceName == override.PackageReferenceName { + matches = append(matches, match{priority: 90, packageID: p.PackageID, packageReferenceName: p.PackageReferenceName}) + } else { + matches = append(matches, match{priority: 40, packageID: p.PackageID}) + } + } else if p.PackageReferenceName != "" && p.PackageReferenceName == override.PackageReferenceName { + matches = append(matches, match{priority: 80, packageReferenceName: p.PackageReferenceName}) + } + } + + if len(matches) == 0 { + return nil, fmt.Errorf("could not resolve step name or package matching %s", actionNameOrPackageID) + } + sort.SliceStable(matches, func(i, j int) bool { // want a stable sort so if there's more than one possible match we pick the first one + return matches[i].priority > matches[j].priority + }) + + return &PackageVersionOverride{ + ActionName: matches[0].actionName, + PackageID: matches[0].packageID, + PackageReferenceName: matches[0].packageReferenceName, + Version: override.Version, + }, nil +} + +func ApplyPackageOverrides(packages []*StepPackageVersion, overrides []*PackageVersionOverride) []*StepPackageVersion { + for _, o := range overrides { + packages = applyPackageOverride(packages, o) + } + return packages +} + +func applyPackageOverride(packages []*StepPackageVersion, override *PackageVersionOverride) []*StepPackageVersion { + if override.Version == "" { + return packages // not specifying a version is technically an error, but we'll just no-op it for safety; should have been filtered out by ParsePackageOverrideString before we get here + } + + var matcher func(pkg *StepPackageVersion) bool = nil + + switch { + case override.PackageID == "" && override.ActionName == "": // match everything + matcher = func(pkg *StepPackageVersion) bool { + return true + } + case override.PackageID != "" && override.ActionName == "": // match on package ID only + matcher = func(pkg *StepPackageVersion) bool { + return pkg.PackageID == override.PackageID + } + case override.PackageID == "" && override.ActionName != "": // match on step only + matcher = func(pkg *StepPackageVersion) bool { + return pkg.ActionName == override.ActionName + } + case override.PackageID != "" && override.ActionName != "": // match on both; shouldn't be possible but let's ensure it works anyway + matcher = func(pkg *StepPackageVersion) bool { + return pkg.PackageID == override.PackageID && pkg.ActionName == override.ActionName + } + } + + if override.PackageReferenceName != "" { // must also match package reference name + if matcher == nil { + matcher = func(pkg *StepPackageVersion) bool { + return pkg.PackageReferenceName == override.PackageReferenceName + } + } else { + prevMatcher := matcher + matcher = func(pkg *StepPackageVersion) bool { + return pkg.PackageReferenceName == override.PackageReferenceName && prevMatcher(pkg) + } + } + } + + if matcher == nil { + return packages // we can't possibly match against anything; no-op. Should have been filtered out by ParsePackageOverrideString + } + + result := make([]*StepPackageVersion, len(packages)) + for i, p := range packages { + if matcher(p) { + result[i] = &StepPackageVersion{ + PackageID: p.PackageID, + ActionName: p.ActionName, + PackageReferenceName: p.PackageReferenceName, + Version: override.Version, // Important bit + } + } else { + result[i] = p + } + } + return result +} + +// Note this always uses the Table Printer, it pays no respect to outputformat=json, because it's only part of the interactive flow +func printPackageVersions(ioWriter io.Writer, packages []*StepPackageVersion) error { + // step 1: consolidate multiple rows + consolidated := make([]*StepPackageVersion, 0, len(packages)) + for _, pkg := range packages { + + // the common case is that packageReferenceName will be equal to PackageID. + // however, in advanced cases it may not be, so we need to do extra work to show the packageReferenceName too. + // we suffix it onto the step name, following the web UI + qualifiedPkgActionName := pkg.ActionName + if pkg.PackageID != pkg.PackageReferenceName { + //qualifiedPkgActionName = fmt.Sprintf("%s%s", qualifiedPkgActionName, output.Yellowf("/%s", pkg.PackageReferenceName)) + qualifiedPkgActionName = fmt.Sprintf("%s/%s", qualifiedPkgActionName, pkg.PackageReferenceName) + } else { + qualifiedPkgActionName = fmt.Sprintf("%s%s", qualifiedPkgActionName, output.Dimf("/%s", pkg.PackageReferenceName)) + } + + // find existing entry and insert row below it + updatedExisting := false + for index, entry := range consolidated { + if entry.PackageID == pkg.PackageID && entry.Version == pkg.Version { + consolidated = append(consolidated[:index+2], consolidated[index+1:]...) + consolidated[index+1] = &StepPackageVersion{ + PackageID: output.Dim(pkg.PackageID), + Version: output.Dim(pkg.Version), + ActionName: qualifiedPkgActionName, + } + updatedExisting = true + break + } + } + if !updatedExisting { + consolidated = append(consolidated, &StepPackageVersion{ + PackageID: pkg.PackageID, + Version: pkg.Version, + ActionName: qualifiedPkgActionName, + }) + } + } + + // step 2: print them + t := output.NewTable(ioWriter) + t.AddRow( + output.Bold("PACKAGE"), + output.Bold("VERSION"), + output.Bold("STEP NAME/PACKAGE REFERENCE"), + ) + //t.AddRow( + // "-------", + // "-------", + // "---------------------------", + //) + + for _, pkg := range consolidated { + version := pkg.Version + if version == "" { + version = output.Yellow("unknown") // can't determine version for this package + } + t.AddRow( + pkg.PackageID, + version, + pkg.ActionName, + ) + } + + return t.Print() +} + +func AskPackageOverrideLoop( + packageVersionBaseline []*StepPackageVersion, + defaultPackageVersion string, // the --package-version command line flag + initialPackageOverrideFlags []string, // the --package command line flag (multiple occurrences) + asker question.Asker, + stdout io.Writer) ([]*StepPackageVersion, []*PackageVersionOverride, error) { + packageVersionOverrides := make([]*PackageVersionOverride, 0) + + // pickup any partial package specifications that may have arrived on the commandline + if defaultPackageVersion != "" { + // blind apply to everything + packageVersionOverrides = append(packageVersionOverrides, &PackageVersionOverride{Version: defaultPackageVersion}) + } + + for _, s := range initialPackageOverrideFlags { + ambOverride, err := ParsePackageOverrideString(s) + if err != nil { + continue // silently ignore anything that wasn't parseable (should we emit a warning?) + } + resolvedOverride, err := ResolvePackageOverride(ambOverride, packageVersionBaseline) + if err != nil { + continue // silently ignore anything that wasn't parseable (should we emit a warning?) + } + packageVersionOverrides = append(packageVersionOverrides, resolvedOverride) + } + + overriddenPackageVersions := ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) + +outerLoop: + for { + err := printPackageVersions(stdout, overriddenPackageVersions) + if err != nil { + return nil, nil, err + } + + // While there are any unresolved package versions, force those + for _, pkgVersionEntry := range overriddenPackageVersions { + if strings.TrimSpace(pkgVersionEntry.Version) == "" { + + var answer = "" + for strings.TrimSpace(answer) == "" { // if they enter a blank line just ask again repeatedly + err = asker(&survey.Input{ + Message: output.Yellowf("Unable to find a version for \"%s\". Specify a version:", pkgVersionEntry.PackageID), + }, &answer, survey.WithValidator(func(ans interface{}) error { + str, ok := ans.(string) + if !ok { + return errors.New("internal error; answer was not a string") + } + if !isValidVersion(str) { + return fmt.Errorf("\"%s\" is not a valid version", str) + } + return nil + })) + + if err != nil { + return nil, nil, err + } + } + + override := &PackageVersionOverride{Version: answer, ActionName: pkgVersionEntry.ActionName, PackageReferenceName: pkgVersionEntry.PackageReferenceName} + if override != nil { + packageVersionOverrides = append(packageVersionOverrides, override) + overriddenPackageVersions = ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) + } + continue outerLoop + } + } + + // After all packages have versions attached, we can let people freely tweak things until they're happy + + // side-channel return value from the validator + var resolvedOverride *PackageVersionOverride = nil + var answer = "" + err = asker(&survey.Input{ + Message: "Package override string (y to accept, u to undo, ? for help):", + }, &answer, survey.WithValidator(func(ans interface{}) error { + str, ok := ans.(string) + if !ok { + return errors.New("internal error; answer was not a string") + } + + switch str { + // valid response for continuing the loop; don't attempt to validate these + case "y", "u", "r", "?", "": + return nil + } + + ambOverride, err := ParsePackageOverrideString(str) + if err != nil { + return err + } + resolvedOverride, err = ResolvePackageOverride(ambOverride, packageVersionBaseline) + if err != nil { + return err + } + + return nil // good! + })) + + // if validators return an error, survey retries itself; the errors don't end up at this level. + if err != nil { + return nil, nil, err + } + + switch answer { + case "y": // YES these are the packages they want + break outerLoop + case "?": // help text + _, _ = fmt.Fprintf(stdout, output.FormatDoc(packageOverrideLoopHelpText)) + case "u": // undo! + if len(packageVersionOverrides) > 0 { + packageVersionOverrides = packageVersionOverrides[:len(packageVersionOverrides)-1] + // always reset to the baseline and apply everything in order, there's less room for logic errors + overriddenPackageVersions = ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) + } + case "r": // reset! All the way back to the calculated versions, discarding even the stuff that came in from the cmdline + if len(packageVersionOverrides) > 0 { + packageVersionOverrides = make([]*PackageVersionOverride, 0) + overriddenPackageVersions = ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) + } + default: + if resolvedOverride != nil { + packageVersionOverrides = append(packageVersionOverrides, resolvedOverride) + // always reset to the baseline and apply everything in order, there's less room for logic errors + overriddenPackageVersions = ApplyPackageOverrides(packageVersionBaseline, packageVersionOverrides) + } + } + // loop around and let them put in more input + } + return overriddenPackageVersions, packageVersionOverrides, nil +} diff --git a/pkg/util/util.go b/pkg/util/util.go index 48515a7e..658ae8db 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -1,6 +1,9 @@ package util -import "fmt" +import ( + "fmt" + "slices" +) // SliceContains returns true if it finds an item in the slice that is equal to the target func SliceContains[T comparable](slice []T, target T) bool { @@ -217,3 +220,43 @@ func HumanReadableBytes(b int64) string { } return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) } + +// SplitString splits the input string into components based on delimiter characters. +// we want to pick up empty entries here; so "::5" and ":pterm:5" should both return THREE components, rather than one or two +// and we want to allow for multiple different delimeters. +// neither the builtin golang strings.Split or strings.FieldsFunc support this. Logic borrowed from strings.FieldsFunc with heavy modifications +func SplitString(s string, delimiters []rune) []string { + // pass 1: collect spans; golang strings.FieldsFunc says it's much more efficient this way + type span struct { + start int + end int + } + spans := make([]span, 0, 3) + + // Find the field start and end indices. + start := 0 // we always start the first span at the beginning of the string + for idx, ch := range s { + if slices.Contains(delimiters, ch) { + if start >= 0 { // we found a delimiter and we are already in a span; end the span and start a new one + spans = append(spans, span{start, idx}) + start = idx + 1 + } else { // we found a delimiter and we are not in a span; start a new span + if start < 0 { + start = idx + } + } + } + } + + // Last field might end at EOF. + if start >= 0 { + spans = append(spans, span{start, len(s)}) + } + + // pass 2: create strings from recorded field indices. + a := make([]string, len(spans)) + for i, span := range spans { + a[i] = s[span.start:span.end] + } + return a +} From 0b80fc3d3cb7562c165d04e90c5bb022d6d525b3 Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Fri, 1 Nov 2024 15:42:02 +1100 Subject: [PATCH 2/3] Fix channel rule version query parameters --- pkg/cmd/release/create/create.go | 4 +++- pkg/packages/packages.go | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/release/create/create.go b/pkg/cmd/release/create/create.go index 154897ba..7d560c84 100644 --- a/pkg/cmd/release/create/create.go +++ b/pkg/cmd/release/create/create.go @@ -351,7 +351,7 @@ func createRun(cmd *cobra.Command, f factory.Factory, flags *CreateFlags) error // to use as a baseline. The package version override process takes this as an input and layers on top of it func BuildPackageVersionBaselineForChannel(octopus *octopusApiClient.Client, deploymentProcessTemplate *deployments.DeploymentProcessTemplate, channel *channels.Channel) ([]*packages.StepPackageVersion, error) { - result, err := packages.BuildPackageVersionBaseline(octopus, deploymentProcessTemplate.Packages, func(packageRef releases.ReleaseTemplatePackage, query feeds.SearchPackageVersionsQuery) { + result, err := packages.BuildPackageVersionBaseline(octopus, deploymentProcessTemplate.Packages, func(packageRef releases.ReleaseTemplatePackage, query feeds.SearchPackageVersionsQuery) (feeds.SearchPackageVersionsQuery, error) { // look in the channel rules for a version filter for this step+package rulesLoop: @@ -367,6 +367,8 @@ func BuildPackageVersionBaselineForChannel(octopus *octopusApiClient.Client, dep } } } + + return query, nil }) if err != nil { diff --git a/pkg/packages/packages.go b/pkg/packages/packages.go index 8a230452..6e5f605a 100644 --- a/pkg/packages/packages.go +++ b/pkg/packages/packages.go @@ -65,7 +65,8 @@ type StepPackageVersion struct { // BuildPackageVersionBaseline loads the deployment process template from the server, and for each step+package therein, // finds the latest available version satisfying the channel version rules. Result is the list of step+package+versions // to use as a baseline. The package version override process takes this as an input and layers on top of it -func BuildPackageVersionBaseline(octopus *octopusApiClient.Client, packages []releases.ReleaseTemplatePackage, setAdditionalFeedQueryParameters func(releases.ReleaseTemplatePackage, feeds.SearchPackageVersionsQuery)) ([]*StepPackageVersion, error) { +func BuildPackageVersionBaseline(octopus *octopusApiClient.Client, packages []releases.ReleaseTemplatePackage, + setAdditionalFeedQueryParameters func(releases.ReleaseTemplatePackage, feeds.SearchPackageVersionsQuery) (feeds.SearchPackageVersionsQuery, error)) ([]*StepPackageVersion, error) { result := make([]*StepPackageVersion, 0, len(packages)) // step 1: pass over all the packages in the deployment process, group them @@ -129,7 +130,14 @@ func BuildPackageVersionBaseline(octopus *octopusApiClient.Client, packages []re PackageID: packageRef.PackageID, Take: 1, } - setAdditionalFeedQueryParameters(packageRef, query) + + if setAdditionalFeedQueryParameters != nil { + query, err = setAdditionalFeedQueryParameters(packageRef, query) + + if err != nil { + return nil, err + } + } if cachedVersion, ok := cache[query]; ok { result = append(result, &StepPackageVersion{ From fdee46317eafc555107515b526e8a294718668ba Mon Sep 17 00:00:00 2001 From: Geoff Lamrock Date: Tue, 19 Nov 2024 14:22:24 +1100 Subject: [PATCH 3/3] Fix comment --- pkg/packages/packages.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/packages/packages.go b/pkg/packages/packages.go index 6e5f605a..1b015c66 100644 --- a/pkg/packages/packages.go +++ b/pkg/packages/packages.go @@ -62,9 +62,10 @@ type StepPackageVersion struct { PackageReferenceName string } -// BuildPackageVersionBaseline loads the deployment process template from the server, and for each step+package therein, -// finds the latest available version satisfying the channel version rules. Result is the list of step+package+versions -// to use as a baseline. The package version override process takes this as an input and layers on top of it +// BuildPackageVersionBaseline takes in a set of template packages from the server, and for each step+package therein, +// finds the latest available version. Additional parameters for the feed query can be supplied using the setAdditionalFeedQueryParameters callback. +// Result is the list of step+package+versions to use as a baseline. +// The package version override process takes this as an input and layers on top of it func BuildPackageVersionBaseline(octopus *octopusApiClient.Client, packages []releases.ReleaseTemplatePackage, setAdditionalFeedQueryParameters func(releases.ReleaseTemplatePackage, feeds.SearchPackageVersionsQuery) (feeds.SearchPackageVersionsQuery, error)) ([]*StepPackageVersion, error) { result := make([]*StepPackageVersion, 0, len(packages))