Thursday, 15 August 2013

Windows PowerShell for Developers #4: Housekeeping

In my last posting, we looked at how to automate the creation and publishing of NuGet packages from Visual Studio. We wrote a simple PowerShell script that was invoked when a project finished building, and this packaged the output and pushed it to a network share.

One of the side effects of this automation is that every time you build a new version of the assembly, a new package will be created and pushed out.  NuGet package names  follow the convention
<assembly name>.<major version number>.<minor version number>.<revision>.<build>.nupkg
 so the package name will change every time you build a new version of the DLL.  Before you know it, you're swamped with various package versions, like this screenshot of the repository folder shows:



So, what to do about it?  PowerShell comes to the rescue again.  Here's the contents of a purge.ps1 file I created:

param ( [string]$workingDir = "."
 )

pushd $workingDir
echo "Purging $workingDir"
[string]$pattern = "(?<rootname>.*)\.(?<revision>\d+\.\d+\.\d+\.\d+)\.nupkg";

$results = dir .| Sort-Object Name -Descending   | Where-Object {$_.Name -match $pattern} |  foreach-object {
    new-object PSObject -Property @{
        rootname = $matches.rootname
        revision = $matches.Revision
        File = $_
  }
 }
$filesToKeep = $results | sort rootname, revision  -Descending | group rootname |
ForEach-Object { $_.group | select -first 1 -ExpandProperty File}

Remove-Item *.nupkg -Exclude $filesToKeep

Write-Output "Files retained are" 
dir .
popd
echo "Purge completed"

Copy this file to your repository folder, fire up a PowerShell console and run it.  It will delete all but the most recent version of the package.

How does it work?  Pretty simply as it happens:
  1. It creates a regex which matches sections of the filenames
  2. It then applies the regex to the current directory, and creates a stream of PowerShell objects that capture the name of the package and the version information separately
  3. It groups these objects by package name and keeps the most recent version in a list called $filesToKeep
  4. It then deletes all packages from the target directory except those in the list $filesToKeep
Imagine doing this in VBScript or, God forbid, the DOS command language...

PowerShell has a pretty arcane syntax, and it's easy to lose sight of what a script is intended to do.  But it's easy to translate a process expressed in plain English into PowerShell, so my advice is simply to write down what you are trying to achieve in words first before plunging into script.

Happy scripting!

Windows PowerShell For developers #3: Fun with NuGet


One of the most frequent complaints I've heard from Visual Studio developers moving to a Java development environment such as Eclipse is the amount of time it takes to get everything set up.  I don't happen to think that the .NET Framework is inherently superior or inferior to Java, just built with a different  set of priorities.  However, it is undeniable that Visual Studio works out-of-the-box.  With a Java project you find yourself spending days having to scrounge various bits and pieces of software and then possibly weeks getting it all to work together.

What's the reason for this discrepancy?  Well, Visual Studio is produced and controlled by one corporation which has tight controls over its features.  Java has,  like Topsy, just 'growed'.  The language specification has been under tight central control but everything else has simply sprouted around it, thanks to a vibrant open-source community.

I happen to think that the involvement of the community in building the product is a good thing, and even Microsoft is starting to embrace this philosophy.  Encouragingly, it is, from the outset, supporting the development of a 'distribution infrastructure' so that people can find components and install them easily.  No more scrounging around the trash cans of the Internet, we hope.

Welcome to NuGet

Part of this new infrastructure is NuGet (pronounced 'nugget').  NuGet is a package manager that runs inside Visual Studio (and is an open source project).  NuGet allows easy sourcing and installation for shared binary components.  It has many nice features, such as automatic updating and dependency management.

This next image is taken from the NuGet home page, showing packages that can be downloaded and installed into a Visual Studio project:
Manage NuGet Packages Dialog Window
You can install NuGet from CodePlex  .  Once you install it, you have access to a wide variety of NuGet packages.  When you install these, you Visual Studio project is automatically updated with references to the packaged components, and it's a doddle to keep them up-to-date. 

NuGet in the Enterprise

NuGet isn't just for hobbyist developers.  If you work for a company that has  a suite of internally developed components, you can package these with NuGet and deploy them to an internal network share.  This can make your life as a developer a hell of a lot easier. 

I'll share with you some PowerShell tools I have developed to make the process easier.

Automating the Process

If you have a solution with a lot of library projects, these are prime candidates for packaging.  The logical time to generate a new package is immediately after the build, and this is best done automatically for each project.

Let's assume you've created a .nuspec file for each project and populated its tags, that you know how to manually create a package,  and that the projects package smoothly and without errors.

So, let's automate the packaging process:

Now, open a project's Properties page, click on the Compile tab and then click the Build Events button and then Edit Post-Build.  Enter the following command line:



Create a file nupack.ps1 in the solution's root folder using a text editor and containing the following PowerShell code:

param(
 [string]$projectdir,
 [string]$config="Debug",
 [string]$outDir
)
if($config -eq "Release")
{
 write-output "Project Directory = $projectdir"
 pushd $projectdir
 write-output "Deleting the packages..."
 del $projectdir\*.nupkg
 write-output "Repackaging..."
 nuget.exe pack -Properties OutDir=$outDir
 popd
xcopy /Y /C "$projectdir*.nupkg" "\\servername\sharename"
}

Substitute '\\server\sharename' for a locally accessible share where you want to store the package.  You can configure the Package Manager to use this location as a repository.

Every time you now build the project in Release configuration, the package will be rebuilt and pushed to the share ready to be picked up by the Package Manager ready for installation into other projects.