Table of contents
Package content
The basic structure of a chocolatey package is:
- packageName.nuspec: metadata of the package
-
tools: folder containing content and scripts
- chocolateyinstall.ps1: executed when a new package or version is installed (choco install packageName)
- [chocolateyuninstall.ps1]: executed when the package is uninstalled (choco uninstall packageName)
- [chocolateyBeforeModify.ps1]: executed when instaling a new version before the new chocolateyinstall.ps1 (from the older version).
- [other files]: installers (exe, msi, etc), zips, etc
NOTE: Only the chocolateyinstall.ps1 script is strictly required.
Package creation
In order to create multiple packages, we created a script that is able to create multiple (or one) packages.
We decided to organize the packages into 3 main groups: system, apps and pipeline.
The list of packages that belong to each group and their parameters are detailed in system.yml, pipeline.yml and apps.yml.
The scripts:
- Reads the YAML files, assumed to be defined at the same level.
- Creates a list of the packages that are going to be created
- Creates the package directory using the templates (also defined at the same level, but with a symbolic link to C:\ProgramData\chocolatey\templates)
- Copies the missing files from the package’s correspoding content directory (PATH_TO_SCRIPT \ PACKAGE_GROUP \ PACKAGE_NAME).
For example, for the maya2014 package, its corresponding content directory is: .\contents\apps\maya2014 - Creates the package from its directory (basically zip)
- Finally, copies the package to the package repository
The script can be called in 3 different ways:
- Without any parameters, meaning it will create ALL packages defined in all 3 YAML files:
$ python packageCreator.py
- It can also create ONLY the packages from a group by calling the script with the group’s name as a parameter.
$ python packageCreator.py system
OR
$ python packageCreator.py apps.yml
- The same is possible if a list of package names are passed as parameters.
$ python packageCreator.py maya2014 maya2016
OR
$ python packageCreator.py vztrayservice
Templates
The following are the chocolateyinstall.ps1 files of the different templates currently used in our Repository.
All the [[someName]] are variables that will be replaced by those detailed in the yaml files.
Unique installer
- name: mediareader
version: 1.0
template: installer\unique
InstallerName: MediaReader
FileType: msi
SilentArgs: /QB- ALLUSERS=1
ValidExitCodes: "@(0,3010)"
In the unique installer the main function is Install-ChocolateyInstallPackage which uses the parameters in packageArgs to execute the installer.
$groupName = '[[GroupName]]'
$packageName = '[[PackageName]]'
$fileType = '[[FileType]]'
$silentArgs = '[[SilentArgs]]'
$validExitCodes = [[ValidExitCodes]]
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$installerName = '[[InstallerName]]'
$fileLocation = Join-Path $toolsDir "$installerName"
Write-Host "About to install $groupName $packageName"
$packageArgs = @{
packageName = "$packageName"
fileType = "$fileType"
file = $fileLocation
silentArgs = $silentArgs
validExitCodes = $validExitCodes
}
Install-ChocolateyInstallPackage @packageArgs
Dual installer
- name: vcredist2015
version: 14.0.24215.1
template: installer\dual
InstallerName: vcredist_x86
InstallerName64: vcredist_x64
FileType: exe
SilentArgs: /install /passive /norestart
ValidExitCodes: "@(0,3010)"
When there is a version for a 32-bit system and 64-bit system and both are required for the 64-bit system (all vcredists work this way). We install the 32/64 according to the machine’s system and IF the system is 64 bits, we force the execution of the 32 bit installer.
$groupName = '[[GroupName]]'
$packageName = '[[PackageName]]'
$fileType = '[[FileType]]'
$silentArgs = '[[SilentArgs]]'
$validExitCodes = [[ValidExitCodes]]
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$installerName = '[[InstallerName]]'
$installerName64 = '[[InstallerName64]]'
$fileLocation = Join-Path $toolsDir "$installerName"
$fileLocation64 = Join-Path $toolsDir "$installerName64"
Write-Host "About to install $groupName $packageName"
$packageArgs = @{
packageName = "$packageName"
fileType = "$fileType"
file = $fileLocation
file64 = $fileLocation64
silentArgs = $silentArgs
validExitCodes = $validExitCodes
}
Install-ChocolateyInstallPackage @packageArgs
if (Get-ProcessorBits 64) { #Install x86 as well for 64 bits machines
$packageArgs = @{
packageName = "$packageName"
fileType = "$fileType"
file = $fileLocation
silentArgs = $silentArgs
validExitCodes = $validExitCodes
}
Install-ChocolateyInstallPackage @packageArgs
}
Multiple installer
- name: quicktime
version: 1.0
template: installer\multiple
InstallerNames: "@('AppleApplicationSupport', 'AppleSoftwareUpdate', 'QuickTime')"
FileTypes: "@('msi', 'msi', 'msi')"
SilentArgs: "@('/passive', '/passive', '/passive')"
ValidExitCodes: "@((0,3010), (0,3010), (0,3010))"
When a packages requires the execution of multiple installers, then we can use a template that executes a list of installers; each with its own name, file type and exit codes.
$groupName = '[[GroupName]]'
$packageName = '[[PackageName]]'
$validExitCodes = [[ValidExitCodes]]
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$installerNames = [[InstallerNames]]
$fileTypes = [[FileTypes]]
$silentArgs = [[SilentArgs]]
For ($i=0; $i -lt $installerNames.Length; $i++) {
$installer = $installerNames[$i]
$fileLocation = Join-Path $toolsDir $installer
Write-Host "About to install $groupName $packageName's $installer"
$packageArgs = @{
packageName = "$packageName"
fileType = $fileTypes[$i]
file = $fileLocation
silentArgs = $silentArgs[$i]
validExitCodes= $validExitCodes[$i]
}
Install-ChocolateyInstallPackage @packageArgs
}
Zip General
- name: ftrack
version: 1.0.0
template: zip\general
ComponentExtractDir: "C:\\Program Files\\VetorLobo\\ftrack"
In the general zip template the main function is Get-ChocolateyUnzip which unzips the files in $file and extracts them into $extractDir. Another important feature is the use of a helpers module, which includes tqo empty functions named: Package-Pre-Setup and Package-Post-Setup
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$packageName = '[[PackageName]]'
$extractDir = "[[ComponentExtractDir]]"
$zipName = "[[PackageName]]"
$groupName = '[[GroupName]]'
$file = Join-Path $toolsDir "$zipName.zip"
Write-Host "About to load helpers $toolsDir"
. $toolsDir\helpers.ps1
Write-Host "About to install the $groupName $packageName inside $extractDir"
Package-Pre-Setup $packageName $toolsDir
Get-ChocolateyUnzip -PackageName $packageName -FileFullPath $file -Destination $extractDir
Package-Post-Setup $packageName $toolsDir
Helpers.ps1:
function Package-Pre-Setup($packageName, $toolsDir)
{
}
function Package-Post-Setup($packageName, $toolsDir)
{
}
Zip Vetorlobo
- name: vztrayservice
version: 1.0.0
template: zip\vetorlobo
- name: maya2016modules
version: 1.0.0
template: zip\vetorlobo
SubDirectory: Maya2016
For the vetorlobo template the extractDir defaults to `“C:\Program Files\VetorLobo\VetorLobo”.
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$packageName = '[[PackageName]]'
$extractDir = 'C:\Program Files\VetorLobo\VetorLobo'
$subDir = '[[SubDirectory]]'
if($subDir -notlike "*SubDirect*"){
$extractDir += "\$subDir"
}
$zipName = "[[PackageName]]"
$groupName = '[[GroupName]]'
$file = Join-Path $toolsDir "$zipName.zip"
Write-Host "About to load helpers $toolsDir"
. $toolsDir\helpers.ps1
Write-Host "About to install the $groupName $packageName inside $extractDir"
Package-Pre-Setup $packageName $toolsDir
Get-ChocolateyUnzip -PackageName $packageName -FileFullPath $file -Destination $extractDir
Package-Post-Setup $packageName $toolsDir
General
- name: maya2017-standalone
version: 1.0.0
template: general
When the logic of chocolateyinstall.ps1 differs from all the previous templates, then the general one can be used. It only defines the package structure. When the content files are copied, a chocolateyinstall.ps1 must be included.
Updating a package
To update a package you need to:
- Update the contents of the package (PATH_TO_SCRIPT \ PACKAGE_GROUP \ PACKAGE_NAME).
1.1.If the type of package is a ZIP, create a new zip with the new modified content.
1.2 If the type of package is an install, replace the installer with the new one.
1.3 If the helper.ps1 script needs to be updated, replace it with a new one. - Update the version of the package at the package’s group YAML. Use the semantic versioning section to decide how to update the version of the package.
- Update the titel and description of the package’s release at the package’s group YAML
Semantic versioning
We are going to use a semantic versioning approach. This means that each package will be described by a ternary set of numbers: X.Y.Z
- X: MAJOR
- Y: MINOR
- Z: PATCH
We modify each number if
- PATCH: we make small internal changes that fix bugs, but do not change the public interface of the modules. (backwards compatible)
- MINOR: new features or substancial improvements are made available to the public interface or if a function is going to be deprecated (deprecation before complete removal!). Resets PATCH number (backwards compatible)
- MAJOR: if the changes are not backwards compatible (may include MINOR/PATCH changes). Resets MINOR and PATCH numbers.