How can I design a different deployment process for major and minor versions of my software in Octopus Deploy?

I have a deployment process modelled in Octopus which involves 25+ steps. Every time we create a new major release (e.g. from v12.0 to v13.0) we deploy out all of the deployment steps in the project.

New features of our software take time to complete, and in the meantime, the business has asked us to find a way to support deploying minor releases (patches or other changes) which may only involve needing to deploy one or two steps.

This needs to be done in a way that keeps both any downtime to a minimum and also doesn’t require too much cognitive load on our users to understand.

We basically need two types of process for each project, one for major releases, and one for minor patch releases. We’ve looked at channels and we don’t think that will fit our requirements.

We’re not keen on duplicating or splitting projects out for a major and minor release process as we need to able to see in the dashboard what version has been deployed to which customer in a single place.

Is there any way to support this type of complete and partial deployment process within the same project in Octopus?

Firstly, it’s important to understand that we don’t generally recommend deploying projects partially. The reason for this is fairly simple; it becomes very difficult to know exactly what has been deployed from both the dashboard overview or project dashboard.

However, with that being said we appreciate that there are situations where you may want to do this.

There are a couple of options in Octopus you can leverage to help deploy using the all-or-some approach.

Solution 1 - Excluded steps deployment option

This solution makes use of the built-in Excluded steps option in the deployment screen:

Here you can use the checkboxes to select steps to exclude from the deployment.

For a major release, you wouldn’t need to select any of the steps to be excluded. For a patch or minor release, you can select all of the steps you don’t wish to deploy.

This is by far the simplest option and allows for granular control for user’s wishing to exclude steps in a deployment. For this reason, it’s the recommended approach for simple cases.

Solution 2 - Prompted variables, output variables and variable run conditions

The alternative option is to make use of:

In this example, we will make use of a run a script step a that determines whether a complete or partial deployment is required. This step will set output variables that can then be used in run conditions in the subsequent deployment steps.

Prompted variables will be used in the Production environment to allow the user the ability to control whether or not Application A or Application B’s steps should be deployed.

This approach has the benefit of being able to be more selective in which steps run in your deployment process for either a major or patch version being deployed. The downside of this approach is that your deployment process arguably becomes more complex and intricate. You should consider these pros and cons when you decide if it’s appropriate for your situation.

For the purposes of this solution:

  • A major version will be considered a SemVer major increment, e.g. from 2.0.0.0 to 3.0.0.0.
  • Anything else will be considered a patch (or minor) version, e.g. from 1.0.4.0 to 1.0.5.0

In our example deployment process we’ll have the following steps that should only be deployed on a major version deployment:

In addition, steps 4 - 7 highlighted below will be deployed only either on a major or a patch version deployment:

The reason for having the steps above run on both a major or a patch version deployment is to facilitate both a complete and a partial deployment of the project.

Check deployment version - script step

Firstly we need to create a script step which works out which version is being deployed. It contains the following PowerShell code:

$ReleaseNumberBeingDeployed = $OctopusParameters['Octopus.Release.Number']
$ReleaseNumberAlreadyDeployedInEnvironment = $OctopusParameters['Octopus.Release.CurrentForEnvironment.Number']

$newerMajorVersion = $false
$patchVersion = $false

if([string]::IsNullOrWhitespace($ReleaseNumberAlreadyDeployedInEnvironment)) 
{
	Write-Host "First deployment to environment. Newer Major version automatically set to true..."
	$newerMajorVersion = $true
    $patchVersion = $false
}
else {
  # Handle any pre-release tags
  $idx = $ReleaseNumberBeingDeployed.IndexOf("-")

  if($idx -ge 0) 
  {
      $ReleaseNumberBeingDeployed = $ReleaseNumberBeingDeployed.Substring(0, $idx)
  }

  $currentReleaseVersion = New-Object System.Version($ReleaseNumberBeingDeployed)

  $idx = $ReleaseNumberAlreadyDeployedInEnvironment.IndexOf("-")

  if($idx -ge 0) 
  {
      $ReleaseNumberAlreadyDeployedInEnvironment = $ReleaseNumberAlreadyDeployedInEnvironment.Substring(0, $idx)
  }

  $envReleaseVersion = New-Object System.Version($ReleaseNumberAlreadyDeployedInEnvironment)

  if($currentReleaseVersion.Major -gt $envReleaseVersion.Major)
  {
      Write-Host "Newer major version is being deployed: $currentReleaseVersion"
      $newerMajorVersion = $true
  }
  else {
      Write-Host "Major version is not higher, assuming minor / patch is being deployed"
      $patchVersion = $true
  }
}

# In this example, we want to deploy an application if:
# - A newer Major version is being deployed, or
# - A patch version is being deployed
$shouldDeployApps = ($newerMajorVersion -or $patchVersion)

Set-OctopusVariable -name "NewerMajorVersion" -value $newerMajorVersion
Set-OctopusVariable -name "PatchVersion" -value $patchVersion
Set-OctopusVariable -name "ShouldDeployApps" -value $shouldDeployApps

The script step above is written in PowerShell but can be converted to a language of your choice. It simply compares two versions with one another:

  • Octopus.Release.Number - this is the version of the project release version being deployed
  • Octopus.Release.CurrentForEnvironment.Number - this is the project release version of the project that is already deployed to the Environment.

By comparing these two Octopus system variables, we can make a decision as to whether the version about to be deployed is higher than the version currently deployed to that same environment. This allows us to qualify is this deployment is a major or a patch version deployment.

We then set the result of this comparison in an Output variable called NewerMajorVersion.
It also sets two other output variables:

  1. PatchVersion - this is essentially the inverse value of the NewerMajorVersion output variable
  2. ShouldDeployApps - this variable will be used later in a variable run condition to help control whether or not Applications A or B should be deployed as part of this deployment.

Set-up Project variables

The output variables created in the first script step can be referenced in project variables. Doing this can make your variable run conditions easier to understand by hiding some of the complexity away.

We need to add the following variables to support our variable run conditions:

Name Value Scope Description
NewerMajorVersion #{Octopus.Action[Check deployment version].Output.NewerMajorVersion} Determines if we are deploying a major version
PatchVersion #{Octopus.Action[Check deployment version].Output.PatchVersion} Determines if we are deploying a patch version
Project.Deploy.Application.A #{if Project.Prompt.Deploy.Application.A}#{ShouldDeployApps}#{/if} Determines if Application A will be deployed
Project.Deploy.Application.B #{if Project.Prompt.Deploy.Application.B}#{ShouldDeployApps}#{/if} Determines if Application B will be deployed
Project.Prompt.Deploy.Application.A True User selectable checkbox to deploy App A
False :speech_balloon: Production
Project.Prompt.Deploy.Application.B True User selectable checkbox to deploy App B
False :speech_balloon: Production
ShouldDeployApps #{Octopus.Action[Check deployment version].Output.ShouldDeployApps} Determine if Apps should be deployed (for major or patch version)

Note: The :speech_balloon: above indicates a prompted variable of type Checkbox.

The variables Project.Prompt.Deploy.Application.A and Project.Prompt.Deploy.Application.B have two values. One value is scoped to the Production environment. This is done to allow lower environments to be deployed to with minimal user interaction. You could, of course, change these to suit your requirements.

Add variable run conditions to our steps

Next, we need to add a variable run condition to our steps. Remember we have two types of steps to consider:

  1. Steps which should always run on a major version deployment.
  2. Steps which should always run on either a major or a patch deployment. Added to these steps is the ability to choose which of the owning applications (A or B) will be run by the use of the prompted variables we created earlier.

Add run condition to steps which always run on major version

Since we need to add the run condition to the steps which always run on a major version deployment, we use the variable NewerMajorversion by choosing the Run Condition in the step, and selecting the Variable option and add the following expression:

#{if NewerMajorVersion == "True" }true#{/if}

We apply that to the steps Pre-Application step - Deploy when major release and Post-Application step - Deploy when major release like below:

Add run condition to application steps

Since we need to add the run condition to the steps which run on either a major or a patch version deployment, we use a different variable to the one used for the major version variable run condition.

For steps with Application A in the name we use the following variable expression:

#{if Project.Deploy.Application.A == "True" }true#{/if}

For steps with Application B in the name we use the following variable expression:

#{if Project.Deploy.Application.B == "True" }true#{/if}

Choosing which application to deploy:

When running the deployment to the Production, you will be prompted for a choice of which applications to deploy:

Example deployment of a minor version

In the screenshot below, Tenant A previously had version 1.0.4 deployed to Production, and now we are deploying version 1.0.5:

Since this was considered a patch version:

  • Steps 3 and 8 are skipped as they only run on a major version deployment.
  • Application A is deployed since we opted to deploy by checking the box of the using the prompted variable.
  • Application B is skipped since we opted to only deploy Application A.

Example deployment of a major version

In the screenshot below, Tenant C previously had version 1.0.4 deployed to Production, and now we are deploying version 2.0.6:

Since this was considered a major version all steps were deployed.

It would have been possible to still choose to prevent either Application A or B from being deployed using the prompted variables, but that wasn’t utilized.

:white_check_mark: Octopus Samples instance

Check out our Samples instance to see this example (login as Guest).