How To Create an Extension Package
How To Create an Extension Package
As we’ve mentioned in other articles, a Chocolatey package can do near anything PowerShell can - including sharing functions and logic between packages.
In this how-to, we’ll talk about sharing functions between packages using extension packages.
Back To Basics Livestream
The creation, and usage, of Chocolatey extension packages was a topic of a recent back-to-basics livestream. The recording of that livestream can be found here:
What Is an Extension Package
In Chocolatey-parlance, an extension package is a package that extends the ability of other Chocolatey packages, by making additional PowerShell functions or cmdlets available.
Most simply put, a given extension package will import one or more PowerShell modules every time choco
runs!
Why Would You Use an Extension Package
The main reason we talk about extension packages is to reduce having to add the same lines of code or functions to multiple packages you maintain.
You may be familiar with DRY (“Don’t Repeat Yourself”) coding principles, which (put simply) hold that there are benefits to maintaining your code in a single location and calling that where appropriate.
Extension packages allow you to easily follow this best practice within your Chocolatey packages without copying your lines of code into dozens of places (or packages).
Examples of this include:
-
The Chocolatey Font Helpers Extension, which has some well tested functionality for installing and uninstalling fonts in Chocolatey packages.
-
The Chocolatey Compatibility Extension, which provides otherwise deprecated or removed functions from older releases of Chocolatey CLI to maintain compatibility with older packages.
-
The Chocolatey Windows Update extension, which provides shared functions for many of the Windows Knowledge Base update packages on the Chocolatey Community Repository.
You might choose to use this over installing a full PowerShell module if you didn’t want to expose the PowerShell module to the normal users of the computer - for instance, if you never wanted to expose the functions outside of Chocolatey runspaces. If you just wanted to use an available PowerShell module, you might consider having a dependency on a package that installs that module, instead.
Extension Package
An extension package is organised much like any other Chocolatey package - though instead of the scripts within the tools
directory you may be familiar with, there should be an extensions
directory.
We’ll now run through how to create an extension package, assuming you have an environment set up as in Preparing Your Environment for Package Creation.
Creating an Extension Package
Shortcut: You can use a package template to create an extension package! Try it, by installing the
extension.template
package and runningchoco new test.extension --template extension
The extension package is actually different enough to a regular package that using the default template would result in more cleanup than help!
Follow the steps below to get started:
- Create a new folder for this package. For the example, we will call it
example.extension
.- Create a package metadata file named
example.extension.nuspec
, and add content as shown below.
- Create a package metadata file named
- In the
example.extension
folder, create a folder namedextensions
.- Within the
extensions
folder, create aExampleModule.psm1
file, and add content as shown below.
- Within the
Package Metadata Content
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>example.extension</id>
<version>0.1.0</version>
<title>Example Extension</title>
<authors>__REPLACE__</authors>
<description>A super cool extension package!</description>
<summary>Extension package for testing.</summary>
<tags>extension chocolatey package</tags>
</metadata>
</package>
Simple Module Content
function Set-ConfigValue {
<#
.Synopsis
Sets a value for that program you're using.
.Example
Set-ConfigValue -Name SMTP -Value 6
#>
[CmdletBinding()]
param(
# The name of the config to set
[Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName)]
[string]$Name,
# The value to set the config to
[Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName)]
[string]$Value,
# The path to the config file to modify
[string]$Path = "C:\ProgramData\exampleapp\config.json"
)
begin {
# Ensure the Config Directory exists
if (-not (Test-Path (Split-Path $Path))) {
$null = New-Item -Path (Split-Path $Path) -ItemType Directory
}
$Configuration = if (Test-Path $Path) {
Get-Content $Path | ConvertFrom-Json
} else {
@{}
}
}
process {
$Configuration.$Name = $Value
}
end {
$Configuration | ConvertTo-Json | Set-Content -Path $Path
}
}
So, if you browse through the files in VS Code or Explorer, you should see a file structure like this:
example.extension
├── example.extension.nuspec
├── extensions
│ ├── ExampleModule.psm1
Open the example.extension.nuspec
file in VS Code and modify the metadata as appropriate:
- authors: This should be replaced with your name or handle.
That’s all you need!
Creating Your Install Script
As we mentioned earlier, there’s no need for an install script in an extension package - Chocolatey CLI handles putting the extension in the right place!
You can also include a tools
directory, with the standard Chocolatey scripts (which could be used for configuration of the module), but it is not required.
Considering Uninstallation
Chocolatey will also handle uninstallation of the extension module automatically, so there’s no need to add a chocolateyUninstall.ps1
script to this package.
Compiling Your Package
You can now use choco pack
to compile your Chocolatey package, creating a file with a .nupkg
extension, ready for installation!
- In VS Code, press
Ctrl + Shift + P
or use the View menu and click on Command Palette. - Select
Chocolatey: Package Chocolatey package(s)
from the prompt. - Select
example.extension.nuspec
from the prompt. - In Additional Arguments enter
--output-directory='~\tutorials'
(or whichever directory you’re using for these tutorials), and press Enter.
You should have a new package generated in your current working directory.
Installing Your Package
This sort of package isn’t normally installed on it’s own, but you can now test installation of your package!
In an elevated PowerShell command prompt, run the following:
choco install example.extension --source='tutorials' -y
You should then be able to browse to the extensions directory, within your Chocolatey installation, and see that your PowerShell module has been placed there.
# This should show an 'example' directory, along with all of your other installed extensions
Get-ChildItem $env:ChocolateyInstall\extensions
# This should show your module
Get-ChildItem $env:ChocolateyInstall\extensions\example
Where Are Extensions Stored?
Chocolatey packages are stored within the lib
folder, of the Chocolatey installation location.
This is the C:\ProgramData\chocolatey\lib
folder by default.
However, Chocolatey CLI understands packages that end with .extension
and some additional processing is completed.
The actual extension package contents will be extracted, and stored, in the C:\ProgramData\chocolatey\extensions
directory.
The name of the folder will match the name you gave to the extension package.
For example, an extension package with the name chocolatey-windowsupdate.extension
, will, by default, have the package contents extracted to C:\ProgramData\chocolatey\extensions\chocolatey-windowsupdate
.
This special handling for packages ending with .extension
also applies to packages that end with .hook
and .template
.
For .hook
packages, by default, there is a folder at C:\ProgramData\chocolatey\hooks
.
For .template
packages, by default, there is a folder at C:\ProgramData\chocolatey\templates
.
Using Shared PowerShell Functions
Once you’ve created your extension package, using the functions contained within is as simple as ensuring the package is installed (for instance, by adding a dependency) and calling the functions within your Chocolatey scripts.
If you’ve already created a config package, why not try adding your newly available functions to it? We’ll use it as an example, here, but you can apply the same ideas to other packages.
- Open the config package
.nuspec
file in VS Code. - Add a dependency on your new extension package.
- Replace usage of PowerShell code in the original package with your new function.
Nuspec Dependency Addition
Your final .nuspec
file should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. -->
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>example-config</id>
<version>0.1.0</version>
<title>Example Configuration</title>
<authors>PackageMaintainer</authors>
<tags>example config-only</tags>
<summary>A package that configures 'example'</summary>
<description>Further detail about the configuration that is applied, and any package parameters.</description>
<dependencies>
<dependency id="example.extension" />
</dependencies>
</metadata>
<files>
<file src="tools\**" target="tools" />
</files>
</package>
Note the new <dependencies>
section, near the bottom! There are a lot of ways to specify specific versions, ranges of versions, and more - see here for more detail.
You should now be able to reference your function within the package, without having defined it there.
PowerShell Dependency Addition
# Write Configuration File
Set-ConfigValue -Name LicensePath -Value (Join-Path $ConfigDirectory "LICENSE.txt")
Set-ConfigValue -Name ComputerName -Value $env:ComputerName
Conclusion
At this point, you have created a package that extends the functionality of Chocolatey CLI.