How to build, test, & deploy your PowerShell projects on Linux and macOS for free with Travis CI
PowerShell Core on Travis CI
By Troy Lindsay
- 11 minutes read- 2143 words
Last month, I started a new pet project of building an open-source PowerShell module for Armor, and one of the first goals that came to mind was that I wanted to ensure compatibility with PowerShell Core on Linux.
I had recently re-read Chris Wahl’s article: How to Version and Publish a PowerShell Module to GitHub and PSGallery with AppVeyor, and figured that there had to be a similar service for Linux, so I started looking around.
I found Travis CI rather quickly, and was pleasantly surprised to discover that they offered macOS images in addition to Ubuntu.
Travis CI is a hosted, distributed continuous integration service used to build and test projects hosted at GitHub. Travis CI automatically detects when a commit has been made and pushed to a GitHub repository that is using Travis CI, and each time this happens, it will try to build the project and run tests. This includes commits to all branches, not just to the main branch.
Restated, this means that every time you push new code up to your public repo, Travis CI (and/or AppVeyor) will build your project per your specifications, run any tests defined, and even deploy it if desired.
For free.
Build, test, and deploy on push for free.
How cool is that?
Now, one of the reasons that I’m writing this article is that getting started with building & testing a PowerShell project on Travis CI was not intuitive.
AppVeyor and Travis CI were both designed for building, testing, and deploying programming language projects, not scripting language projects.
It took a lot of RTFM and a little trial & error to figure it out, but it was so worth it.
The following article covers some of my lessons learned in the process.
I hope that you find them valuable.
Getting started with Travis CI
Sign into Travis CI with your GitHub account and accept the GitHub access permissions confirmation.
Once you’re signed in to Travis CI, and we’ve synchronized your GitHub repositories, go to your profile page and enable the repository you want to build:
Add a .travis.yml file to your repository to tell Travis CI what to do.
Add the .travis.yml file to git, commit and push, to trigger a Travis CI build:
Travis only runs builds on the commits you push after you’ve enabled the repository in Travis CI.
Check the build status page to see if your build passes or fails, according to the return status of the build command
Not too bad, right? Don’t worry, there isn’t that much more even though the scroll bar indicates otherwise.
Travis CI’s documentation of the .travis.yml file sprawls quite a bit as there are so many features available, so I’ll start with an example .travis.yml config file that should work for testing most of your open-source PowerShell projects on the Travis CI platform.
In my next post, I will provide a high-level overview of all of the available options that I found in the documentation for reference, as well as my design decisions for the ArmorPowerShell project.
Example .travis.yml config file
To start testing your open-source PowerShell project on macOS & Ubuntu, copy the contents below to a file named .travis.yml in the base directory of your project.
The PowerShell Core executable name has been shortened to pwsh as of v6.0.0-beta.9.
There are a few lines that call PowerShell to execute a file, such as pwsh -f "${env:TRAVIS_BUILD_DIR}/install-dependencies.ps1" in the base directory of the project, fully-pathed through the TRAVIS_BUILD_DIRenvironment variable, but these are by no means necessary.
You could store these files in sub-directories, give the files different names, call commands instead of files, or do something else entirely- these are all just ideas to stimulate your imagination; however, whatever logic you define needs to be valid or your build will fail.
What does this .travis.yml config file do?
language: This defines the programming language that the build system should use.
I set this to generic, because I’m building a scripting language project.
matrix: The Matrix section allows you to customize each image that will build your code.
include: Include the specified image configurations.
All configurations defined for an image in the matrix will override the corresponding global configuration.
For example, I configured a before_install section in the osx image above, so if I had a global before_install section defined in the .travis.yml config file, the macOS image would skip it.
Excludes can also be defined for here more complex build topologies.
os: The operating system of the image.
As of 20171125, the two choices are osx (macOS) and linux (Ubuntu).
dist: The Ubuntu Linux distro image.
As of 20171125, the two choices are trusty (Ubuntu 14.04 LTS Trusty Tahr) and precise (Ubuntu 12.04 LTS Precise Pangolin, which is end of life).
sudo: This purpose of this setting is almost certainly not what you think.
Setting sudo to false in your Trusty images causes your build to be deployed to a container instead of a VM, which will start up and complete much faster than the VM image.
Unfortunately, as of 20171125, there is not a containerized option for macOS yet.
If you want or need to use an Linux VM image, set sudo to required.
I have had no issues with building or testing my code inside a container so far.
I will update the article if I discover a blocking issue, but I don’t expect to at this point since the PowerShell Core team publishes nightly builds on Docker Hub and of note, they also build on Travis CI.
addons: There is a lot that can be configured in the addons section, but for now, we’re only going to use this for the Trusty image to add the appropriate Microsoft software repository where the official PowerShell Core binaries are hosted, the software repository key, and to install PowerShell Core per the recommended methodology as defined in the PowerShell Core Install Guide.
apt: The default package management tool for Ubuntu.
sources: Software repositories to add.
sourceline: The software repository configuration.
key_url: The public key for encrypting the traffic.
packages: Software packages to install.
powershell: Install PowerShell Core on Linux, please and thank you.
osx_image: The macOS image that you want to use.
As of 20171125, the Travis CI default is 7.3, which is an older macOS 10.11 image.
The official PowerShell install guide only lists support for macOS 10.12; however, I have performed a few basic functional tests on osx images: 7.3 (10.11) & 6.4 (10.10). PowerShell Core installed, and completed the build and test runs successfully without any additional configuration on macOS 10.11, but failed on macOS 10.10.
PowerShell Core may work on macOS 10.10 with additional configuration, but I’m not interested in researching this any further at this time.
If you are concerned about breaking changes between macOS versions, you can duplicate the osx matrix image section and replace the value of osx_image with a different version.
before_install: This matrix image section overrides the global before_install configuration for our osx image, and is used for installing PowerShell Core as defined in the installation guide.
Homebrew-Cask extends Homebrew and brings its elegance, simplicity, and speed to macOS applications and large binaries alike.
brew cask install powershell: Install PowerShell Core on macOS, please and thank you.
fast_finish: Job failures halt the build.
If you would rather have the build attempt to continue on error, change the value to false.
install: This section can be used for calling the PowerShell script to install dependencies, such as any modules needed to build and/or test the script.
I highly recommend storing the logic for each section in a separate file so that:
It is easier for you to reuse & maintain code if you choose to also integrate with AppVeyor for testing your open-source project for free on Windows PowerShell as well, and also…
…because of the inherent challenges with embedding code in code.
The build lifecycle order of operations has the install section follow the before_install section and precedes the before_script section.
before_script: This section can be used for calling your PowerShell build script to do things such as update the module manifest, update the documentation, et cetera.
Here’s my build.ps1 script for the ArmorPowerShell project.
script: This section can be used for calling your PowerShell unit, integration, and/or functional test scripts.
If you are new to these concepts, I recommend reading up on those topics, as well as Pester.
Here’s my start-tests.ps1 script for the ArmorPowerShell project.
after_success: This section can be used for calling a deployment script if that makes sense for your project, such as publishing your module, script, et cetera to the PowerShell Gallery, NuGet, Chocolatey, GitHub Releases, et cetera.
Travis CI is an extremely powerful platform with tons of other features that you can take advantage of, but that is all that I’m going to cover in this post as to the possibilities available in the .travis.yml config file.
Lint your .travis.yml config file
Now, it’s time to test your config file using the Travis Client that we installed earlier by running travis post.
travis post
Warnings for .travis.yml:
[x] in matrix.include section: unexpected key osx_image, dropping
[x] in matrix.include section: unexpected key dist, dropping
[x] in matrix.include section: unexpected key sudo, dropping
Wait, what? Why am I seeing these warnings?
As of 20171125, this type of build matrix image configuration is recommended per the Travis CI multiple operating system build configurations documentation, but it will generate three false positive unexpected key warnings when linting your .travis.yml config file.
These three warnings can be disregarded and have been reported here.
Any warnings or errors other than these should be addressed.
Commit your .travis.yml config file
When you are ready, run the following commands to:
Stage the ./travis.yml config file to the index
Commit the ./travis.yml config file
And then push it up to the main branch of your GitHub public repo, which will trigger the first Travis CI build for your project!
If you prefer to push the change to a branch other than main, then update the branch name accordingly.
Now that you have configured and run your first build, update your GitHub repository settings so that any contributions to your project must first pass your build and testing framework as a prerequisite for consideration.
To do so:
Go to https://github.com/<user>/<project>/settings/branches/<branch>/
Enable Require status checks to pass before merging.
Also recommended:
Enable Require pull request reviews before merging.
Enable Require review from Code Owners.
Enable Require branches to be up to date before merging.
You made it!
Voila! You’re done!
There are plenty of other things that you can do here such as configure notifications so that Travis CI automatically posts your build results in a Slack channel, publish your PowerShell module on successful build to the PowerShell gallery, or add a badge to your README.md indicating whether the last build passed or failed, all of which I’ll cover in the next post, but you should have enough now to start testing your PowerShell project on macOS and Ubuntu for free on the powerful & versatile Travis CI platform.