Skip to content

Instantly share code, notes, and snippets.

@lmiller1990
Created December 16, 2024 04:33
Show Gist options
  • Save lmiller1990/7c2455f675dec98efc06f2bfafdf114e to your computer and use it in GitHub Desktop.
Save lmiller1990/7c2455f675dec98efc06f2bfafdf114e to your computer and use it in GitHub Desktop.
devcontainers

I am getting started with devcontainers - I have too many projects, and managing dependencies and versions is getting too tiring. We can automate it with devcontainers.

Example: With Java

I work with Java lately, let's start with that. Let's create a project

mkdir HelloWorld
mkdir -p com/example/HelloWorld
touch com/example/HelloWorld/Main.java

Add some code:

package com.example.HelloWorld;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
    }
}

Make sure it works:

javac com/example/HelloWorld/Main.java
java com.example.HelloWorld.Main #=> Hello, Java!

Note: The convention for java is <namespace>.Mainfile . Mainfile must be a .java file with a public static void main(String[] args) function. Otherwise you get this error:

Error: Main method not found in class com.example.HelloWorld.Main, please define the main method as:
   public static void main(String[] args)

Now, let's do it again, but this time with a devcontainer.

devcontainer.json

devcontainer is a specification. You write json to describe how and what your stack requires. It uses docker, by default. Read more here.

The convention is to create a .devcontainer directory with a devcontainer.json file. My full project:

.
├── .devcontainer
│   └── devcontainer.json
└── com
    └── example
        └── HelloWorld
            └── Main.java

Here is a basic devcontainer.json:

{
  "name": "Java DevContainer",
  "image": "mcr.microsoft.com/devcontainers/java:20",
  "mounts": ["source=${localWorkspaceFolder},target=/workspace,type=bind"],
  "workspaceFolder": "/workspace"
}

name and image are pretty straight forward. mounts is more interesting. The syntax is

{
	"mounts": [
	  "source=<local-path>,target=<container-path>,type=<mount-type>"
	]
}
  • localWorkspaceFolder, when using the devcontainer CLI, is what you pass to --workspace-folder.
  • target is the directory in the container where your localWorkspaceFolder will be linked.
  • type is either bind or volume. bind is what we want for local development.

Build it!

devcontainer build --workspace-folder . --no-cache

Now the container is built. That is where your code will run, and you will do development.

Now we start the container.

devcontainer up --workspace-folder . -- bash

Finally, we can "go into" the container with exec:

devcontainer exec --workspace-folder . -- bash

We are in /workspace by default. That comes from the "workspace" folder in the devcontainer.json file. The user is vscode by default, at least on my machine.

vscode ➜ /workspace $ pwd
/workspace

vscode ➜ /workspace $ tree -a .
.
├── com
│   └── example
│       └── HelloWorld
│           └── Main.java
└── .devcontainer
    └── devcontainer.json

5 directories, 2 files

Doing Work

We can simply edit the files in the container using vim , or even using your own IDE - the changes will be synced both ways, since we are using type=bind in mounts.

Automation

You can use VS Code to alleviate the need do build / start / exec the container with the devcontainer extension.

First, stop the existing container. Mine is 5e7c52cc282b. You can do it using docker. devcontainer CLI seems to lack this feature.

docker ps
CONTAINER ID   IMAGE                                     COMMAND                  CREATED          STATUS          PORTS     NAMES
5e7c52cc282b   mcr.microsoft.com/devcontainers/java:17   "/bin/sh -c 'echo Co…"   20 minutes ago   Up 20 minutes             quirky_raman

Stop it:

docker container stop 5e7c52cc282b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment