Create a Release with PowerShell Zipped Artifacts and Chocolatey

In previous post I described how to create a simple PowerShell scripts that is capable of installing a software starting from a zipped file that contains the “release” of a software (produced by a build) and some installation parameters. Once you have this scenario up and running, releasing your software automatically is quite simple.

Once you automated the installation with a PowerShell script plus an archive file with Everything is needed to install the software, you are only a step away from Continuous Deployment.

A possible release vector is Chocolatey , a package manager for Windows based on NuGet. I’m not a Chocolatey expert, but at the very basic level, to create a chocolatey package you should only have a bunch of files and a PowerShell script that install the software.

The most basic form of a Chocolatey package is composed by files that contains artifacts to be installed and a PowerShell script that does the installation.

The very first step is creating a.nuspec file that will define what will be contained in Chocolatey package. Here is a possible content of release.nuspec file.

<package>
  <metadata>
  …
  </metadata>

<files>
        <file src=”Tools\Setup\chocolateyInstall.ps1″ target=”Tools” />
        <file src=”Tools\Setup\ConfigurationManagerSetup.ps1″ target=”Tools” />
        <file src=”Tools\Setup\JarvisUtils.psm1″ target=”Tools” />
        <file src=”release\Jarvis.ConfigurationService.Host.zip” target=”Artifacts” />
  </files>

</package>

The real interesting part of this file is the <files> section, where I simply list all the files I want to be included in the package. ConfigurationManagerSetup.ps1 and JarvisUtils.psm1 are the original installation script I wrote, and Jarvis.ConfigurationService.Host.zip is the name of the artifacts generated by the PackRelease.ps1 script.

To support Chocolatey you only need to create another script, called chocolateyInstall.ps1 that will be called by chocolatey to install the software. This script is really simple, it needs to parse Chocolatey input parameters and invoke the original installation script to install the software.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Write-Output "Installing Jarvis.ConfigurationManager service. Script running from $PSScriptRoot"
$packageParameters = $env:chocolateyPackageParameters
Write-Output "Passed packageParameters: $packageParameters"
$arguments = @{}
# Script used to parse parameters is taken from https://github.com/chocolatey/chocolatey/wiki/How-To-Parse-PackageParameters-Argument
# Now we can use the $env:chocolateyPackageParameters inside the Chocolatey package
$packageParameters = $env:chocolateyPackageParameters
# Default the values
$installationRoot = "c:\jarvis\setup\ConfigurationManager\ConfigurationManagerHost"
# Now parse the packageParameters using good old regular expression
if ($packageParameters) {
    $match_pattern = "\/(?([a-zA-Z]+)):(?([`"'])?([a-zA-Z0-9- _\\:\.]+)([`"'])?)|\/(?([a-zA-Z]+))"
    $option_name = 'option'
    $value_name = 'value'
    Write-Output "Parameters found, parsing with regex";
    if ($packageParameters -match $match_pattern )
    {
       $results = $packageParameters | Select-String $match_pattern -AllMatches
       $results.matches | % {
            $arguments.Add(
                $_.Groups[$option_name].Value.Trim(),
                $_.Groups[$value_name].Value.Trim())
       }
    }
    else
    {
        Throw "Package Parameters were found but were invalid (REGEX Failure)"
    }
    if ($arguments.ContainsKey("installationRoot")) {
        $installationRoot = $arguments["installationRoot"]
        Write-Output "installationRoot Argument Found: $installationRoot"
    }
} else 
{
    Write-Output "No Package Parameters Passed in"
}
Write-Output "Installing ConfigurationManager in folder $installationRoot"
$artifactFile = "$PSScriptRoot\..\Artifacts\Jarvis.ConfigurationService.Host.zip"
if(!(Test-Path -Path $artifactFile ))
{
     Throw "Unable to find package file $artifactFile"
}
Write-Output "Installing from artifacts: $artifactFile"
if(!(Test-Path -Path "$PSScriptRoot\ConfigurationManagerSetup.ps1" ))
{
     Throw "Unable to find package file $PSScriptRoot\ConfigurationManagerSetup.ps1"
}
if(-not(Get-Module -name jarvisUtils)) 
{
    Import-Module -Name "$PSScriptRoot\jarvisUtils"
}
&amp; $PSScriptRoot\ConfigurationManagerSetup.ps1 -deployFileName $artifactFile -installationRoot $installationRoot

This script does almost nothing, it delegates everything to the ConfigurationManagerSetup.ps1 file. Once the.nuspec file and this script are writenn, you can use nuget.exe to manually generate a package and verify installing in a target system. When everything is ok and you are able to create and install a chocolatey package locally, you can schedule Chocolatey package creation with a TFS Build. The advantage is automatic publishing and SemVer version management done with Gitversion.exe.

image

Figure 1: Task to generate Chocolatey Package.

To generate Chocolatey Package you can use the standard NuGet Packager task, because Chocolatey uses the very same technology as NuGet. As you can see from Figure 1 the task is really simple and needs very few parameters to work. If you like more detail I’ve blogged in the past about using this task to publish NuGet packages.

Publish a Nuget Package to NuGet/MyGet with VSO Build

This technique is perfectly valid for VSTS or TFS 2015 on-premises. Once the build is finished, and the package is published, you should check on NuGet or MyGet if the package is ok.

image

Figure 2: Package listing on MyGet, I can verify all published versions

Now you can install with this command

1
2
3
4
choco install Jarvis.ConfigurationService.Host
 	-source 'https://www.myget.org/F/jarvis/api/v2'
-packageParameters "/installationRoot:C:\Program files\Proximo\ConfigurationManager" 
   -force 

All parameters needed by the installation scripts should be specified with the –packageParameters command line options. It is duty of chocolateyInstall.ps1 parsing this string and pass all parameters to the original installation script.

If you want to install a prerelease version (the ones with a suffix) you need to specify the exact version and use the –pre parameter.

A working example of scripts and nuspec file can be found in ConfigurationManager project.

Gian Maria.