Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/OctopusDeploy/go-octodiff v1.0.0
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.114.1
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.115.1-0.20260819065856-b6f0f290712d
github.com/bmatcuk/doublestar/v4 v4.10.0
github.com/briandowns/spinner v1.23.2
github.com/google/uuid v1.6.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63n
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/OctopusDeploy/go-octodiff v1.0.0 h1:U+ORg6azniwwYo+O44giOw6TiD5USk8S4VDhOQ0Ven0=
github.com/OctopusDeploy/go-octodiff v1.0.0/go.mod h1:Mze0+EkOWTgTmi8++fyUc6r0aLZT7qD9gX+31t8MmIU=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.114.1 h1:7lrYQSCo2HeixFRBGGKA8a7QjuBw9L+4A4kmUDUEzSE=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.114.1/go.mod h1:VkTXDoIPbwGFi5+goo1VSwFNdMVo784cVtJdKIEvfus=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.115.1-0.20260819065856-b6f0f290712d h1:iCMRfyFo2hKQ0zu95s5T9FUwQQ3ucvQxrjZ8So6uM5c=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.115.1-0.20260819065856-b6f0f290712d/go.mod h1:VkTXDoIPbwGFi5+goo1VSwFNdMVo784cVtJdKIEvfus=
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w=
Expand Down
17 changes: 16 additions & 1 deletion pkg/cmd/release/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const (
FlagAliasGuidedFailureMode = "guided-failure-mode"
FlagAliasGuidedFailureModeLegacy = "guidedFailure"

FlagPriority = "priority"

FlagForcePackageDownload = "force-package-download"
FlagAliasForcePackageDownloadLegacy = "forcePackageDownload"

Expand Down Expand Up @@ -107,6 +109,7 @@ type DeployFlags struct {
UpdateVariables *flag.Flag[bool]
ExcludedSteps *flag.Flag[[]string]
GuidedFailureMode *flag.Flag[string] // tri-state: true, false, or "use default". Can we model it with an optional bool?
Priority *flag.Flag[string] // tri-state: true, false, or "use default"
ForcePackageDownload *flag.Flag[bool]
DeploymentTargets *flag.Flag[[]string]
ExcludeTargets *flag.Flag[[]string]
Expand All @@ -127,6 +130,7 @@ func NewDeployFlags() *DeployFlags {
UpdateVariables: flag.New[bool](FlagUpdateVariables, false),
ExcludedSteps: flag.New[[]string](FlagSkip, false),
GuidedFailureMode: flag.New[string](FlagGuidedFailure, false),
Priority: flag.New[string](FlagPriority, false),
ForcePackageDownload: flag.New[bool](FlagForcePackageDownload, false),
DeploymentTargets: flag.New[[]string](FlagDeploymentTarget, false),
ExcludeTargets: flag.New[[]string](FlagExcludeDeploymentTarget, false),
Expand Down Expand Up @@ -169,6 +173,7 @@ func NewCmdDeploy(f factory.Factory) *cobra.Command {
flags.BoolVarP(&deployFlags.UpdateVariables.Value, deployFlags.UpdateVariables.Name, "", false, "Overwrite the release variable snapshot by re-importing variables from the project.")
flags.StringArrayVarP(&deployFlags.ExcludedSteps.Value, deployFlags.ExcludedSteps.Name, "", nil, "Exclude specific steps from the deployment")
flags.StringVarP(&deployFlags.GuidedFailureMode.Value, deployFlags.GuidedFailureMode.Name, "", "", "Enable Guided failure mode (true/false/default)")
flags.StringVarP(&deployFlags.Priority.Value, deployFlags.Priority.Name, "", "", "Jump the task queue ahead of other queued tasks (true/false/default). Requires the Priority Tasks feature, and the TaskPrioritize permission to set true.")
flags.BoolVarP(&deployFlags.ForcePackageDownload.Value, deployFlags.ForcePackageDownload.Name, "", false, "Force re-download of packages")
flags.StringArrayVarP(&deployFlags.DeploymentTargets.Value, deployFlags.DeploymentTargets.Name, "", nil, "Deploy to this target (can be specified multiple times)")
flags.StringArrayVarP(&deployFlags.ExcludeTargets.Value, deployFlags.ExcludeTargets.Name, "", nil, "Deploy to targets except for this (can be specified multiple times)")
Expand Down Expand Up @@ -203,6 +208,10 @@ func deployRun(cmd *cobra.Command, f factory.Factory, flags *DeployFlags) error
outputFormat = constants.OutputFormatTable
}

if _, err = executionscommon.ParsePriorityMode(flags.Priority.Value); err != nil {
return err
}

octopus, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
if err != nil {
return err
Expand All @@ -223,6 +232,7 @@ func deployRun(cmd *cobra.Command, f factory.Factory, flags *DeployFlags) error
ScheduledExpiryTime: flags.MaxQueueTime.Value,
ExcludedSteps: flags.ExcludedSteps.Value,
GuidedFailureMode: flags.GuidedFailureMode.Value,
Priority: flags.Priority.Value,
ForcePackageDownload: flags.ForcePackageDownload.Value,
DeploymentTargets: flags.DeploymentTargets.Value,
ExcludeTargets: flags.ExcludeTargets.Value,
Expand Down Expand Up @@ -262,6 +272,7 @@ func deployRun(cmd *cobra.Command, f factory.Factory, flags *DeployFlags) error
resolvedFlags.MaxQueueTime.Value = options.ScheduledExpiryTime
resolvedFlags.ExcludedSteps.Value = options.ExcludedSteps
resolvedFlags.GuidedFailureMode.Value = options.GuidedFailureMode
resolvedFlags.Priority.Value = options.Priority
resolvedFlags.DeploymentTargets.Value = options.DeploymentTargets
resolvedFlags.ExcludeTargets.Value = options.ExcludeTargets
resolvedFlags.DeploymentFreezeNames.Value = options.DeploymentFreezeNames
Expand Down Expand Up @@ -297,6 +308,7 @@ func deployRun(cmd *cobra.Command, f factory.Factory, flags *DeployFlags) error
resolvedFlags.MaxQueueTime,
resolvedFlags.ExcludedSteps,
resolvedFlags.GuidedFailureMode,
resolvedFlags.Priority,
resolvedFlags.ForcePackageDownload,
resolvedFlags.DeploymentTargets,
resolvedFlags.ExcludeTargets,
Expand Down Expand Up @@ -1033,6 +1045,8 @@ func PrintAdvancedSummary(stdout io.Writer, options *executor.TaskOptionsDeployR

gfmStr := executionscommon.LookupGuidedFailureModeString(options.GuidedFailureMode)

priorityStr := executionscommon.LookupPriorityString(options.Priority, "Use default setting from the lifecycle phase")

pkgDownloadStr := executionscommon.LookupPackageDownloadString(!options.ForcePackageDownload)

depTargetsStr := "All included"
Expand Down Expand Up @@ -1068,9 +1082,10 @@ func PrintAdvancedSummary(stdout io.Writer, options *executor.TaskOptionsDeployR
Deploy Time: cyan(%s)
Skipped Steps: cyan(%s)
Guided Failure Mode: cyan(%s)
Priority: cyan(%s)
Package Download: cyan(%s)
Deployment Targets: cyan(%s)
`)), deployAtStr, skipStepsStr, gfmStr, pkgDownloadStr, depTargetsStr)
`)), deployAtStr, skipStepsStr, gfmStr, priorityStr, pkgDownloadStr, depTargetsStr)
}

func selectRelease(octopus *octopusApiClient.Client, ask question.Asker, questionText string, space *spaces.Space, project *projects.Project, channel *channels.Channel) (*releases.Release, error) {
Expand Down
104 changes: 102 additions & 2 deletions pkg/cmd/release/deploy/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1861,6 +1861,95 @@ func TestDeployCreate_AutomationMode(t *testing.T) {
assert.Equal(t, "", stdErr.String())
}},

{"release deploy with --priority false sends Priority Off", func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"release", "deploy", "--project", fireProject.Name, "--version", "1.0", "--environment", "dev", "--priority", "false", "--output-format", "basic"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1/projects/"+fireProject.GetName()).RespondWith(fireProject)

req := api.ExpectRequest(t, "POST", "/api/Spaces-1/deployments/create/untenanted/v1")
requestBody, err := testutil.ReadJson[deployments.CreateDeploymentUntenantedCommandV1](req.Request.Body)
assert.Nil(t, err)

assert.Equal(t, deployments.CreateDeploymentUntenantedCommandV1{
ReleaseVersion: "1.0",
EnvironmentNames: []string{"dev"},
CreateExecutionAbstractCommandV1: deployments.CreateExecutionAbstractCommandV1{
SpaceID: "Spaces-1",
ProjectIDOrName: fireProject.Name,
Priority: "Off",
},
}, requestBody)

req.RespondWith(&deployments.CreateDeploymentResponseV1{
DeploymentServerTasks: []*deployments.DeploymentServerTask{
{DeploymentID: "Deployments-203", ServerTaskID: "ServerTasks-29394"},
},
})

_, err = testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)

assert.Equal(t, "ServerTasks-29394\n", stdOut.String())
assert.Equal(t, "", stdErr.String())
}},

{"release deploy with --priority default leaves Priority unset", func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"release", "deploy", "--project", fireProject.Name, "--version", "1.0", "--environment", "dev", "--priority", "default", "--output-format", "basic"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api/").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1/projects/"+fireProject.GetName()).RespondWith(fireProject)

req := api.ExpectRequest(t, "POST", "/api/Spaces-1/deployments/create/untenanted/v1")
requestBody, err := testutil.ReadJson[deployments.CreateDeploymentUntenantedCommandV1](req.Request.Body)
assert.Nil(t, err)

assert.Equal(t, deployments.CreateDeploymentUntenantedCommandV1{
ReleaseVersion: "1.0",
EnvironmentNames: []string{"dev"},
CreateExecutionAbstractCommandV1: deployments.CreateExecutionAbstractCommandV1{
SpaceID: "Spaces-1",
ProjectIDOrName: fireProject.Name,
},
}, requestBody)

req.RespondWith(&deployments.CreateDeploymentResponseV1{
DeploymentServerTasks: []*deployments.DeploymentServerTask{
{DeploymentID: "Deployments-203", ServerTaskID: "ServerTasks-29394"},
},
})

_, err = testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)

assert.Equal(t, "ServerTasks-29394\n", stdOut.String())
assert.Equal(t, "", stdErr.String())
}},

{"release deploy rejects an unrecognised --priority value before contacting the server", func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"release", "deploy", "--project", fireProject.Name, "--version", "1.0", "--environment", "dev", "--priority", "urgent"})
return rootCmd.ExecuteC()
})

_, err := testutil.ReceivePair(cmdReceiver)
assert.EqualError(t, err, "'urgent' is not a valid value for priority, expected true, false or default")

assert.Equal(t, "", stdOut.String())
assert.Equal(t, "", stdErr.String())
}},

{"release deploy specifying all the args; untentanted", func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
Expand All @@ -1873,6 +1962,7 @@ func TestDeployCreate_AutomationMode(t *testing.T) {
"--deploy-at-expiry", "2022-09-10 13:37:03 +10:00",
"--skip", "Install", "--skip", "Cleanup",
"--guided-failure", "true",
"--priority", "true",
"--force-package-download",
"--update-variables",
"--target", "firstMachine", "--target", "secondMachine",
Expand Down Expand Up @@ -1908,6 +1998,7 @@ func TestDeployCreate_AutomationMode(t *testing.T) {
ExcludedMachineNames: []string{"thirdMachine"},
SkipStepNames: []string{"Install", "Cleanup"},
UseGuidedFailure: &trueVal,
Priority: "On",
RunAt: "2022-09-10 13:32:03 +10:00",
NoRunAfter: "2022-09-10 13:37:03 +10:00",
Variables: map[string]string{
Expand Down Expand Up @@ -1947,6 +2038,7 @@ func TestDeployCreate_AutomationMode(t *testing.T) {
"--tenant", "Coke", "--tenant", "Pepsi",
"--tenant-tag", "Region/us-east",
"--guided-failure", "true",
"--priority", "true",
"--force-package-download",
"--update-variables",
"--target", "firstMachine", "--target", "secondMachine",
Expand Down Expand Up @@ -1983,6 +2075,7 @@ func TestDeployCreate_AutomationMode(t *testing.T) {
ExcludedMachineNames: []string{"thirdMachine"},
SkipStepNames: []string{"Install", "Cleanup"},
UseGuidedFailure: &trueVal,
Priority: "On",
RunAt: "2022-09-10 13:32:03 +10:00",
NoRunAfter: "2022-09-10 13:37:03 +10:00",
Variables: map[string]string{
Expand Down Expand Up @@ -2083,7 +2176,7 @@ func TestDeployCreate_GenerationOfAutomationCommand_MasksSensitiveVariables(t *t
// need to very it's wired up properly
receiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer testutil.Close(api, qa)
rootCmd.SetArgs([]string{"release", "deploy", "--project", "fire project", "--version", "2.0", "--environment", "dev"})
rootCmd.SetArgs([]string{"release", "deploy", "--project", "fire project", "--version", "2.0", "--environment", "dev", "--priority", "true"})
return rootCmd.ExecuteC()
})

Expand Down Expand Up @@ -2173,6 +2266,7 @@ func TestDeployCreate_GenerationOfAutomationCommand_MasksSensitiveVariables(t *t
CreateExecutionAbstractCommandV1: deployments.CreateExecutionAbstractCommandV1{
SpaceID: "Spaces-1",
ProjectIDOrName: fireProject.Name,
Priority: "On",
Variables: map[string]string{
"Boring Variable": "BORING",
"Nuclear Launch Codes": "9001",
Expand All @@ -2199,10 +2293,11 @@ func TestDeployCreate_GenerationOfAutomationCommand_MasksSensitiveVariables(t *t
Deploy Time: Now
Skipped Steps: None
Guided Failure Mode: Use default setting from the target environment
Priority: Jump the task queue
Package Download: Use cached packages (if available)
Deployment Targets: All included

Automation Command: octopus release deploy --space 'Default Space' --project 'Fire Project' --version '2.0' --environment 'dev' --variable 'Boring Variable:BORING' --variable 'Nuclear Launch Codes:*****' --variable 'Secret Password:*****' --no-prompt
Automation Command: octopus release deploy --space 'Default Space' --project 'Fire Project' --version '2.0' --environment 'dev' --priority 'true' --variable 'Boring Variable:BORING' --variable 'Nuclear Launch Codes:*****' --variable 'Secret Password:*****' --no-prompt
Warning: Command includes some sensitive variable values which have been replaced with placeholders.
Successfully started 2 deployment(s)

Expand All @@ -2225,6 +2320,7 @@ func TestDeployCreate_PrintAdvancedSummary(t *testing.T) {
Deploy Time: Now
Skipped Steps: None
Guided Failure Mode: Use default setting from the target environment
Priority: Use default setting from the lifecycle phase
Package Download: Use cached packages (if available)
Deployment Targets: All included
`), stdout.String())
Expand All @@ -2234,6 +2330,7 @@ func TestDeployCreate_PrintAdvancedSummary(t *testing.T) {
options := &executor.TaskOptionsDeployRelease{
ScheduledStartTime: "2022-09-23",
GuidedFailureMode: "false",
Priority: "true",
ForcePackageDownload: true,
ExcludedSteps: []string{"Step 1", "Step 37"},
DeploymentTargets: []string{"vm-1", "vm-2"},
Expand All @@ -2246,6 +2343,7 @@ func TestDeployCreate_PrintAdvancedSummary(t *testing.T) {
Deploy Time: 2022-09-23
Skipped Steps: Step 1,Step 37
Guided Failure Mode: Do not use guided failure mode
Priority: Jump the task queue
Package Download: Re-download packages from feed
Deployment Targets: Include vm-1,vm-2; Exclude vm-3,vm-4
`), stdout.String())
Expand All @@ -2262,6 +2360,7 @@ func TestDeployCreate_PrintAdvancedSummary(t *testing.T) {
Deploy Time: Now
Skipped Steps: None
Guided Failure Mode: Use default setting from the target environment
Priority: Use default setting from the lifecycle phase
Package Download: Use cached packages (if available)
Deployment Targets: Include vm-2
`), stdout.String())
Expand All @@ -2278,6 +2377,7 @@ func TestDeployCreate_PrintAdvancedSummary(t *testing.T) {
Deploy Time: Now
Skipped Steps: None
Guided Failure Mode: Use default setting from the target environment
Priority: Use default setting from the lifecycle phase
Package Download: Use cached packages (if available)
Deployment Targets: Exclude vm-4
`), stdout.String())
Expand Down
Loading