Skip to content

Create Release

This section describes how you can create releases within Octopus when using the dynamic package name approach and when you have multiple packages within a single deployment. As mentioned in the overview section, DataStar deployments have more than one package as you have to deploy the release tools, provide templates and deploy the packaged scripts.

Manual Deployment

It is possible to create the releases manually when using the dynamic package name approach, however it requires you to set the overall version and the package version with different values.

Firstly when you create a manual release you need to "Expand All" so you can see the versions for all the packages. If we were deploying our user story package from our library feed (for example DAT-243432.76.nupkg) then we would set the Version to be 243432.76 and the dynamic package version to be 76 - this is shown below:

Automated Release Creation

Whilst the manual release creation is useful for the reversal releases (on the basis that reverting changes should be for exceptional circumstances), it is useful to automate the creation of the deployment releases. Typically this will be done in your CI pipeline once you have created a build.

We have created a Python script which you can use to create a release during your build pipelines, it uses the Octopus REST API so you can adapt this for other languages if you wish. This will perform the following steps:

  1. It looks up the Octopus Space Id given an Octopus Space Name, as this is required to look up project by name.
  2. It looks up the Octopus Project Id within the Octopus Space given an Octopus Project Name.
  3. It looks up the Octopus Channel Id within the Octopus Project given an Octopus Channel Name.
  4. For each of the packages referenced it will look up the latest package and set that version, with the exception of dynamic package names which will be set to use the version specified in the arguments.
  5. Finally it will create a release with the multiple package versions resolved.
import json
import requests
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-b', '--base', type=str, required=True)
parser.add_argument('-a', '--apikey', type=str, required=True)
parser.add_argument('-w', '--workitem', type=int, required=True)
parser.add_argument('-v', '--version', type=int, required=True)
parser.add_argument('-c', '--channel', type=str, required=False)
parser.add_argument('-p', '--project', type=str, required=True)
parser.add_argument('-s', '--space', type=str, required=True)
args = parser.parse_args()

spaceName =
projectName = args.project

channelName = "Default"
    channelName =
workItem = str(args.workitem)
version = str(args.version)
packageVersion = workItem + '.' + version

session = requests.Session()
session.headers.update({'X-Octopus-ApiKey': str(args.apikey)})

response = session.get(str(args.base) + '/spaces/all')
if response.status_code != 200:
    print('Error response ' + str(response.status_code) + ' looking up space ' + args.story)
    raise SystemExit(1)

spaceId = None
spaces = json.loads(response.content)
for item in spaces:
    if item['Name'] == spaceName:
        spaceId = item['Id']

if spaceId is None:
    print('Error response failed to locate spaceId for name ' + spaceName)
    raise SystemExit(1)

response = session.get(str(args.base) + "/" + spaceId + '/projects/all')
if response.status_code != 200:
    print('Error response ' + str(response.status_code) + ' looking up project ' + projectName)
    raise SystemExit(1)

projectId = None
projects = json.loads(response.content)
for item in projects:
    if item['Name'] == projectName:
        projectId = item['Id']

if projectId is None:
    print('Error response failed to locate projectId for name ' + projectName)
    raise SystemExit(1)

response = session.get(str(args.base) + '/projects/' + projectId + "/channels")
if response.status_code != 200:
    print('Error response ' + str(response.status_code) + ' looking up channels')
    raise SystemExit(1)

channelId = None
channels = json.loads(response.content)
for item in channels['Items']:
    if item['Name'] == channelName:
        channelId = item['Id']

if channelId is None:
    print('Error response failed to locate channelId for name ' + channelName)
    raise SystemExit(1)

response = session.get(str(args.base) + '/'
                       + spaceId + '/deploymentprocesses/deploymentprocess-'
                       + projectId + '/template?channel=' + channelId)

# Create the release body
releaseBody = json.loads('{{ "ChannelId": "{0}", "ProjectId": "{1}", "Version": "{2}", "SelectedPackages": [] }}'.format(channelId, projectId, packageVersion))

templates = json.loads(response.content)
for item in templates['Packages']:
    response = session.get(str(args.base) + '/' + spaceId + '/feeds/'
                           + item['FeedId'] + '/packages/versions?packageId='
                           + item['PackageId'] + '&take=1')
    versionSpec = packageVersion
    versionInfo = json.loads(response.content)
    for versionItem in versionInfo['Items']:
        versionSpec = versionItem['Version']
    if versionSpec == packageVersion:
        versionSpec = version

    releaseBody['SelectedPackages'] \
        .append(json.loads('{{ "ActionName": "{0}", "PackageReferenceName": "{1}", "Version": "{2}" }}'
                .format(item['ActionName'], item['PackageReferenceName'], versionSpec)))

print('Creating Release:' + json.dumps(releaseBody))
result = + '/' + spaceId
                      + '/releases?ignoreChannelRules=false', data=json.dumps(releaseBody))

if result.status_code != 200 | result.status_code != 201:
    print('Error response ' + str(result.status_code) + ' message: ' + str(result.content))
    raise SystemExit(1)
Back to top