(Ab)using Docker to support multiple versions of `dotnet`

Anyone who has been (attempting) to follow .NET tooling of late will have noticed that the behaviour is changing quite dramatically, especially for the (very cool) dotnet CLI tool. I mean, how awesome is it to just clone a repo, cd in and then run dotnet build?!

no solution found

Wait, what? Oh, so let's update our copy of dotnet and try out a different project:

no project.json found

Now many of you will be able to spot that this is a very contrived example, but read on!

Note that the rest of this tutorial is mostly using Bash (since I'm on Linux). Either use Bash for Windows, or adjust for PowerShell accordingly

The "problem"

Now I call it a "problem" because it's not a problem per se, it's just progress. As the dotnet tooling matured, there was a parallel change from the project.json project system back to csproj-based MSBuild projects. There have also a been a massive number of small changes to the syntax and logic behind dotnet.

The issue I face is that I work with a wide variety of projects that use a mix of all three tooling systems ('old' csproj, project.json and 'new' csproj), and while I could keep multiple copies of the SDK installed, or rely on global.json and various other things, I like to over-engineer things so this is obviously a problem for Docker!

Everyone knows in 2017, if you're having a problem, you just need more Docker.

The "solution"

The solution to this comes in the form of Microsoft's own official microsoft/dotnet Docker image. Once you've got Docker installed and set up, just run docker pull microsoft/dotnet to fetch the newest dotnet tools ready to go.

Run first, ask questions later

Now, run docker run -it --rm microsoft/dotnet and look on in amazement as you get a clean, up-to-date, dotnet-capable environment!

dotnet --version

Of course, that just gets us a blank slate, so we can add -v $PWD:/app to mount our current directory as /app in our container.

with PWD volume

Tag that

Now so far all we have is a nice way to get the newest dotnet CLI, but that doesn't help us when we find a project.json. The trick here is that Microsoft's Docker image includes a number of tags for different versions of the SDK. Pulling a specific tag of an image doesn't replace any existing images you've pulled so you can use different tags alongside each other.

This is very useful for many other things. Try being able to pull fedora:24 and fedora:23 and run them alongside each other on a Fedora 25 machine!

So to build our project.json project all we need to do is run using the special 1.1.0-sdk-projectjson tag:

dotnet run -it --rm -v $PWD:/app microsoft/dotnet:1.1.0-sdk-projectjson  

Now run your normal dotnet restore -> dotnet build chain and witness as your previously not-working project.json is built before your eyes.

project.json build

Make the leap

Ah, but now you've gone and updated your project to the new csproj format using dotnet migrate! What now?

Well, just swap that 1.1.0-sdk-projectjson tag for latest and build as before:

docker run -it --rm -v $PWD:/app microsoft/dotnet:latest  

csproj build

Conserve your keystrokes

But it gets pretty tiring to type in that massive command every time, right? Well, if you're on Bash, try adding a quick and dirty script like this one:

#!/usr/bin/env bash

TARGET=${2-$PWD}  
echo "Starting dotnet image with tag: $1 in $TARGET"  
docker run -it --rm -v $TARGET:/app microsoft/dotnet:$1  

Save that somewhere in your PATH with a good name (I use dnvm because I'm nostalgic like that) and then from your project directory:

dnvm latest  

This starts a new container, mapping your current directory to /app using the latest dotnet tooling.

dnvm latest

Or, to build that project.json-based project:

dnvm 1.1.0-sdk-projectjson /tmp/json/  

dnvm project.json

If you don't provide a directory as the second argument, it will use your current directory.

Getting creative

Now, you can flesh out your dnvm script with shorthands for specific SDK versions for example, or to use the Windows Containers-powered nanoserver tags (if you're on Windows).

Even better, you can even give specific versions, such as using the 1.0.4-sdk tag in case you have something that was working and is no longer!

Now get out there and start building things!

Comments

comments powered by Disqus