Skip to main content
Background Image
  1. Presentations/

Stop Hand-Rolling Chocolate: Automating Chocolatey with psake

Stop Hand-Rolling Chocolate
#

Automating Chocolatey with psake
#

Gilbert Sanchez

@HeyItsGilbert


Thanks
#


Hey! It’s Gilbert
#

bg right


Hands Up βœ‹
#


“Back in my day…”
#

The year is 2013
#


The Build Process
#

“The ticket queue was the build process.”
#

  • Teams needed a Windows packages built and deployed.
  • They filed a ticket
  • Someone (eventually) hand-crafted it
  • That someone was the only one who knew how

“It Works on My Machine”
#


Except your devs are on Macs
#

  • File locking differently
  • Windows-specific paths, encodings, behaviors
  • Packaging Windows software felt like a foreign language

Extra friction became an extra excuse.


Chocolatey is just PowerShell
#

If you can write a script, you can ship a package.
#


Scaffolding a Package
#

choco new mypackage

mypackage/
β”œβ”€β”€ mypackage.nuspec           ← package metadata
└── tools/
    β”œβ”€β”€ chocolateyInstall.ps1  ← runs on install
    β”œβ”€β”€ chocolateyUninstall.ps1
    └── LICENSE.txt

That’s the whole package.


The Two Files That Matter: Metadata
#

mypackage.nuspec β€” what the package is

<metadata>
  <id>mypackage</id>
  <version>1.0.0</version>
  <description>Does a thing.</description>
</metadata>

The Two Files That Matter: Installer
#

tools/chocolateyInstall.ps1 β€” how it installs

$packageArgs = @{
  packageName = 'mypackage'
  url         = 'https://example.com/installer.exe'
  checksum    = 'ABC123...'
}
Install-ChocolateyPackage @packageArgs

Extensions
#

Add new functions to any package

mycompany-chocolatey-extension/
└── extensions/
    └── mycompany-helpers.psm1  ← imported automatically

Hooks
#

Run logic around every install

mycompany-hooks/
└── hooks/
    β”œβ”€β”€ pre-install-all.ps1     ← before any package installs
    └── post-install-all.ps1    ← after any package installs

bg right contain

psake
#

Tasks with dependencies
#

That’s it. PowerShell Tasks.


Your Build Process is a README and Hope
#


# HOW TO RELEASE (don't forget these steps)
1. Validate the nuspec manually
2. Run choco pack
3. Run the tests (yes, before you push JOHN!)
4. Push to the feed β€” but only on main!

Every step is a chance for someone to skip it.


Manual Steps β†’ Declared Tasks
#

Task Default -depends Pack

Task Test -depends -description "Run Pester tests" {
    Invoke-Pester .\Tests\
}

Task Pack -depends Test -description "Build the .nupkg" {
    exec { choco pack }
}

Task Push -depends Pack -precondition { $env:GITHUB_REF -eq 'refs/heads/main' } {
    exec { choco push mypackage.nupkg --source $env:CHOCO_FEED }
}

The Task Graph
#

Push  (only on main)
 └── Pack
      └── Test

Run everything: Invoke-psake

Run just tests: Invoke-psake -taskList Test


Same command. Local or CI. No surprises.


The Brazil Story
#


The Brazil Story
#

Extensions put your org’s knowledge in one place.

# mycompany-chocolatey-extension/extensions/mycompany-helpers.psm1
function Get-PackageFromCDN {
    param($PackageName, $Version)

    $node   = Resolve-NearestCDNNode          # ← finds SΓ£o Paulo, not Virginia
    $cached = Test-CDNCache -Node $node -Package $PackageName

    if ($cached) { Get-FromCache  -Node $node -Package $PackageName }
    else         { Get-FromOrigin -Package $PackageName -Version $Version }
}

Every package calls Get-PackageFromCDN. Nobody hard-codes the CDN.


Let’s build it
#


Blank repo β†’ published, tested, CI-backed package.


What We Just Built
#

StepWhat it does
.\build.ps1 -Task TestRuns Pester tests against the package
.\build.ps1 -Task PackBuilds the .nupkg
.\build.ps1 -Task PushPublishes β€” but only from main
CI workflowValidates + tests every PR
Publish workflowAuto-publishes on merge

Same psakefile. Local and CI. No surprises.


“I Only Have 5 Packages”
#

You still get:

  • Auto-publish on merge
  • Validated nuspec XML
  • Pester tests
  • Extensions & Hooks

Automation that reduces toil scales down just as well as it scales up.


Your Mac Dev Just Shipped a Windows Package
#


They didn’t touch a Windows machine.

They didn’t learn Chocolatey internals.

They opened a PR. CI went green. They merged.


The packaging knowledge lives in the psakefile β€” not in someone’s head.


Boring is good
#

Predictable. Auditable. Repeatable.
#

The best build pipeline is the one you forget about.


THANK YOU
#

Feedback is a gift
#

Please review this session via the mobile app

Questions? Find me @heyitsgilbert

Gilbert Sanchez
Author
Gilbert Sanchez
Not just good. Good enough.

Related

Efficient Disasters: Remove-Item In the Pipeline
·1171 words·6 mins
I walk through my most painful mistake of 2022. Using Remove-Item in a Pipeline can be efficient, but it can also be a disaster.
Force Reinstalling Many Choco Packages
·565 words·3 mins
Learn how to force reinstall Chocolatey packages using PowerShell and packages.config. Complete walkthrough with XML generation script for handling partially removed packages.
ΒΏNo Habla InglΓ©s?: PowerShell Localization in Practice
·1615 words·8 mins
PowerShell is global, but not everyone works in English. Let’s look at how localization works, how you can add it to your modules, and how to make it easy for your community to contribute translations
Beyond Regex: Advanced PowerShell Code Analysis with ASTs
·1573 words·8 mins
In this post I’ll be walking through an example of a (silly) request you might see at work and show you how you can leverage AST’s to update your codebase.
Sharing Your Custom PSScriptAnalyzer Rules
·467 words·3 mins
Learn how using a simple “proxy” module, will allow you to use custom PSScriptAnalyzer rules in all your repositories!
How to Use Obsidian for ADHD: Productivity System with PowerShell
·1356 words·7 mins
Alleviating my ADHD headaches with Obsidian. Periodic Notes and Templater extensions save the day by reminding me of the next step towards my larger goals.