How to Build & Test Your Next Web Project with PowerShell

…Umm, how do I work on this project again?

PowerShell LogoI’ve worked on many different software projects. Typically I handle them through the course of my job, but there’s been times where I’ve dug into some existing open source projects.

Getting your head around a new project can be daunting. There’s always volumes of new code to digest. No way around it. However, one of the first questions I often wrestle with is, “How do I build and test this thing?”

Every project seems to do it a little different, which is no surprise considering all the build frameworks we developers have available and the ever-evolving ideas and best practices around building. Here at, for example, we use Ruby and Rake on most projects, and some MSBuild. Some web designers like Compass for generating their CSS. We like WebMatrix for working on a marketing site, and Visual Studio for our other web applications. The list goes on…

One “Build” command to rule them all…

I can honestly say that it’s not uncommon for me to jump back into one of my own projects after a leave and not remember how to build it! Somewhere along the line this got me thinking that there has to be a better way.

Can I have just one “build” command that I then customize per project? In other words, I can be in any project folder and type “build” and it initiates a build using whatever is the right way for that project. A batch file named “build.bat” located in the project root directory is an obvious solution, and I did that for a while…

Around this same time period, I also started using the Git and Mercurial source control systems. One feature that really struck me was how they operated on the concept of a “repository” being an entire file and folder tree. In other words, the entire file tree, from the project root all the way down to the leaves, is the default unit of operation and commands work against it in their entirety. For example, I can do a “git status” from the root directory and Git will echo any modified files from the root down. If I do the same command whilst way down deep in some project directory, Git will continue to give the same result.

That’s pretty convenient, and I thought about it every time I found myself cd’ing back to the project root in order to run my build.bat. Could I get that “whole repository” convenience with my build commands?

Of course I can, and I did with PowerShell.

Regardless of what project I am working on, PowerShell is the one place I start for all of them. Project-commands is an open source PowerShell module I created to solve the problem.

Project-commands provides four standard commands useful to software developers. Those are build, test, clean, and develop. You can be anywhere inside your project tree to issue these commands, and they will execute as if they were issued from the root. Each command performs a typical function necessary in most any development environment.  Those are:

BUILD Does all the jobs necessary to create a working system. Compile, generate code, etc.
TEST Test out the system.  Most likely runs all the unit tests.  Might just launch the app instead.
CLEAN Launch the development environment for this project. Very handy when you’re migrating projects to new Visual Studio versions over time.
DEVELOP Launch the development environment for this project. Very handy when you’re migrating projects to new Visual Studio versions over time.

You, as the project developer, write the implementation code for each of these commands. The code will be customized to the specific project and tends to be very short. To implement the clean command say, you’d create a PowerShell function called “clean_project”. Here are a few samples.
function clean_project {
msbuild Amp360.sln /t:Clean

function clean_project {
rake clean

The examples above are taken from two of our projects. Both are one-liners. (I’ve found that’s pretty common.) The first relies on the existing clean commands created for us by Visual Studio and is ran using MSBuild. The second is from one of our projects that uses Rake (a Ruby build system) and runs a clean task that we wrote. You can see that these projects are using wildly different build paradigms. Project-commands lets me forget about that and focus on getting things done.

Here is how it all works:

First, you have to install the project-commands PowerShell Module. Download the files here and drop them into your ~\Documents\WindowsPowerShell\Modules\Project-commands folder.  Place the command Import-Module project-commands at the top of your PowerShell profile file.

Next, go into any directory holding a project. Issue any one of the build, test, clean, or develop commands. The command will fail of course, but it will also initialize your project for use with project-commands.
Project file .\.project-commands.psm1 did not exist, and so was created.
Edit it to add in the specific commands that implement 'build' for this project.
You may want to add .\.project-commands.psm1 to your vcs.

The project-commands module has placed a commands implementation file in your project. This is simply a file named .project-commands.psm1 with nothing more than empty functions for each command and some documentation intended to help you fill them out. Project-commands determined the root folder of your project by searching up the tree for a .git, .hg, or .svn folder. If none of those exist, you’ll need to paste in a copy of .project-commands.psm1 manually. You can file the master copy in the Modules\project-commands folder.

From here forward, project-commands will consult this file when running any of the commands build, test, clean, and develop.

Here is the default output of the develop command:

The 'develop' command has not been created. Edit your .project-commands.psm1 file and add your project specific command(s) to the develop_project function.

Opening up .project-commands for editing will show that the develop_project function by default is pretty much a hello world:

function develop_project {
write-host "The 'develop’ command has not been created. Edit your .project-commands.psm1 file and add your project specific command(s) to the develop_project function."

Rip out that write-host and drop in your implementation. For me that is typically launching Visual Studio and passing it the relevant solution file.

function develop_project {
$env:VS100COMNTOOLS\..\ide\devenv.exe WebSite1.sln

You can see that I am selecting a specific version of Visual Studio. As is often the case, some of our projects have been updated to more recent versions and some have not. If I were to open this solution in a more recent version of VS, it would likely upgrade my project automatically while I was cursing. Then I’d need to back those changes out (more cursing). Project-commands helps me codify this requirement and avoid the mistake in the future.

This is a common implementation of a “test” command around here:

function test_project {
# alias only exists for the lifetime of this function call.
set-alias appcmd     (join-path ${env:ProgramFiles(x86)} "IIS Express\appcmd.exe")
set-alias iisexpress (join-path ${env:ProgramFiles(x86)} "IIS Express\iisexpress.exe")
$name = "ZephyrPortal"
$physicalPath = join-path (pwd) "my-account.Web"
$bindings = "http://localhost:31474"
if(!(appcmd list site /name:"$name")) {
appcmd add site /name:"$name" /bindings:"$bindings" /physicalPath:"$physicalPath"
iisexpress /site:"$name"

In short, this creates a binding in IISExpress (only if it does not exist) and then launches IIExpress, allowing the dev to browse to http://localhost:31474 and test out their web site changes. Our web designers probably haven’t opened Visual Studio in a year and they don’t care to. Nor do they want to get into the xml config file that IISExpress uses.

With project-commands they don’t have to. I set this up one time, and they know to simply run “test” when they want to inspect their work. All that time savings means they can go get more tattoos…

I hope you find project-commands as useful as I do. If you are using it, I’d love to hear about it in the comments!

2015-02-06T08:45:34+00:00 February 6th, 2015|

Leave A Comment