Let’s make builds on Windows a bit friendlier for Unix/Linux types.

VM Based Build Nodes

Consider these when

  • Visual Studio 2013 or older is in the mix
  • You have a lot of files to be processed during build
  • You have a manual build process
  • Your build process depends on build node changes the previous build made

Vagrant and Virtualbox provide a reasonable environment to develop your infrastructure as code describing your build nodes. Rui Lopes provides a clean windows 2016 vagrant box for virtualbox and vsphere.

Ideally most of your Vagrantfile will use provisioners like

config.vm.provision 'whatever',
                    type: 'shell',
                    inline: 'sh -c /vagrant/whatever.sh; if ($LASTEXITCODE -ne 0) {Throw "whatever.sh failed exit code: $($LATEXITCODE)"}'

You won’t need to do as much error handling if you use the catcherror template from the next snippet

These Vagrantfile snippets provide reasonable stop on failure behavior.

catcherror = <<-SCRIPT
  $ErrorActionPreference = 'Stop'
  if ($LASTEXITCODE -ne 0) { Throw "%<cmd>s exit code: $($LASTEXITCODE)"}
bootstrap = format(catcherror, cmd: '/vagrant/bootstrap.bat')

config.vm.provision 'bootstrap',
                    type: 'shell',
                    inline: bootstrap

The catcherror template is cleaner.

This bootstrap.bat provides chocolatey and git and nvm for windows places sh.exe and nvm on the path.

"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command " [System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
if errorlevel 1 (exit /b 1)
choco feature disable --name showDownloadProgress
choco upgrade --install-if-not-installed --allow-downgrade -y git.install --params '/GitAndUnixToolsOnPath'
if errorlevel 1 (exit /b 1)
choco upgrade --install-if-not-installed --allow-downgrade -y nvm.portable
if errorlevel 1 (exit /b 1)
call refreshenv

The chocolatey folks are always refining their one liner. Reference Install with cmd.exe for updates.

And that allows a whatever.sh like

#!/bin/bash -eu
# winrm declares failure when powershell has stderr
exec 2>&1
# For tracing
set -x
Whatever() {
  nvm install 10.16.3
  nvm use 10.16.3
  # Magic sleep waiting for windows symlinks to settle
  sleep 2
  npm install --global windows-build-tools
  exit 0
# Bash acts weird when a scriptfile is edited while it's run
Whatever "$@"

Warnings and gotchas

  • Some versions of git for windows break the ‘cmdThatUsesInputFile <(cmdThatGeneratesInputFileOnStdOut)’ construct
  • You need the chocolatey package ‘findutils’ to have a Unix find instead of the “grep” equivalent for Windows
  • git for windows includes curl. It does not include wget.