Developing in a docker container

Ah, a new open source project that looks interesting. Let's pull it down from GitHub, open it up in the IDE of our choice and.... Oh... It turns out the build scripts assume you have some build tool already installed or, even worse, a specific version of it.

This is a great example of where the Remote Development extension pack for Visual Studio Code comes in. Grab it and you can do all your development in a docker container for your projects!

Naturally, you'll need to have Docker installed, either Docker Desktop for Windows or MacOS, or Docker CE/EE for the Linux distribution of your choosing.

And in even better news, with the release of WSL 2, Docker now uses it instead of Hyper-V as the backend, so you can run Docker on Windows 10 Home Edition too! Read more about it here.

Once that's installed all you need to add is a .devcontainer folder in the root of your project and throw in a devcontainer.json file. You can use it to either specify a prebuilt image or specify a DOCKERFILE that you can use to build a custom development environment.

You can use this for any project in any language, I'm going to detail how I use it for a .NET Core project built in F# but the same ideas can be applied to any project.

You can base your Dockerfile on any image, PHP, NodeJS, the .NET Core SDK, whatever tech stack you're building with.  Then build on it, add in whatever extra tools you need, maybe you want to add git?

A prebuilt environment containing everything you need, with exactly the versions you want. What more could you need?

But wait...there's more!

Why stop at specifying just your build environment? How about what VS Code settings or extensions are needed?

Let's extend the devcontainer.json with some extra instructions. We can say we want some extensions installed by default. If you use VS Code for F# then you probably already have the amazing Ionide plugin installed.

But you want your project to be easy for newcomers to get up and running. Telling them to go install 5 different extensions just get started isn't the best way to start off a relationship with a potential new user or contributor.

Let's just list the extensions we need instead...

{
    "name": "MyAwesomeApp",
    "dockerFile": "Dockerfile",
    "appPort": [8080],
    "extensions": [
        "ionide.ionide-fsharp",
        "ms-vscode.csharp",
        "editorconfig.editorconfig",        
        "ionide.ionide-paket",
        "ionide.ionide-fake"
    ]
} 

What about VS Code settings? All settings in VS Code are specified in json files, so we just add one to the .devcontainer folder and reference it in the Dockerfile.

{
    "FSharp.fsacRuntime":"netcore"
}

This example is named settings.vscode.json so we add it to our Dockerfile

FROM fsharp:netcore

# Copy endpoint specific user settings into container to specify
# .NET Core should be used as the runtime.
COPY settings.vscode.json /root/.vscode-remote/data/Machine/settings.json

# Install git, process tools
RUN apt-get update && apt-get -y install git procps

So now when we open the project in VS Code we get a prompt to reopen it in a container. It will create a Docker container based on the supplied DOCKERFILE, automatically open and forward port 8080 from the container to the host and install all the extensions listed. Now we can happily write our build script that assumes the presence of a build tool and make sure that making changes to it is a smoother experience because VS Code will even have the right extensions for it!

The full reference for what devcontainer.json can do is here, and it can do a lot!