Setting up a devcontainer to work with Jekyll on Windows

6 minute read

In the previous post we set up the repository to work locally. In this post, I will show how to configure Visual Studio Code to work locally on a Windows machine by leveraging Linux containers running on Docker.

Current setup

My home computer is running the latest stable version of Windows 10, Windows 10 May 2020 also known as Windows 10 2004.

Jekyll can’t run natively on Windows and their official documentation instructs using WSL to avoid issues.

Instead, I decided to use a Docker container as a full-featured development environment.

This setup gives me the flexibility to use any computer that meets the prerequisites to work locally or even use GitHub Codespaces in the future.

Prerequisites

The solution highlighted in this post has some requirements:

When all the prerequisites are met, open Visual Studio Code in the folder where you checked out your repository.

Initialize the devcontainer

The first step to initialize the devcontainer is to create its configuration files.

By default, a devcontainer needs two files:

  • A devcontainer.json containing the configuration of the development environment
  • A Dockerfile containing the configuration of the container

Both files are placed in folder called .devcontainer in the root of the workspace.

The Remote Container extension, installed via the Remote Development extension pack, includes many default configurations that can be then customized.

In Visual Studio Code:

  • press F1 to open the palette
  • type and select Remote-Containers: Add Development Container Configuration Files...
  • type and select Show All Definitions...
  • type and select Jekyll (Community)

The extension will then take care of creating the two files.

Visual Studio Code will show a popup asking if you want to reopen the workspace in the remote container. There is not need for it right now.

Before we start customizing the devcontainer setup, you might want to commit the current state.

PS> git add .devcontainers/
PS> git commit -m "Added default devcontainer config"

Here is a guide hosted on Visual Studio Code’s website on how to create a development container.
This page contains information for more advanced scenarios.

Configuring the devcontainer

Before we start using the devcontainer, there are some customizations that can be done.

Once you have done all the customizations you feel necessary, use the Visual Studio Code palette to run the command Remote-Containers: Reopen in Container.

The process of building the container is quite lengthy the first time because the container images needs to be created, customized and the new container needs to be started.

Customization: Timezone

Jekyll uses an environemnt variable to select the timezone used when displaying timestamps.

By default, the container is setup to use America/Chicago as current timezone.

This can be customized in the Dockerfile.

I changed my ENV directive to look like the following

ENV LANG=en_US.UTF-8 \
    LANGUAGE=en_US:en \
    TZ=Europe/Stockholm \
    LC_ALL=en_US.UTF-8 \
    LANG=en_US.UTF-8 \
    LANGUAGE=en_US

Note that I use the Europe/Stockholm timezone now.

Here you can find a list of valid options.

Customization: Add GitHub CLI

I am particularly fond of the new GitHub CLI as it makes it working with a GitHub-backed repository extremely easy.

This guide explains how to quickly add it to a devcontainer.

  • Add the content of this file in .devcontainer/library-scripts/github-debian.sh
  • Append the lines below at the end of the Dockerfile file
COPY library-scripts/github-debian.sh /tmp/library-scripts/
RUN apt-get update && bash /tmp/library-scripts/github-debian.sh

The commands above instruct Docker to download the GitHub CLI and install it when building the container image.

Customization: Markdown linter

Since Jekyll websites are based on Markdown, having a linter for this language can be helpful.

There is an handy extension for Visual Studio Code that enriches the editor with a Markdown linter.

Because it’s an extension that will work inside the devcontainer, we will instruct Visual Studio Code to add this extension to the process running in the container.

We can do this by customizing the devcontainer.json file.

In the file, find the extensions section and add this string to the list: davidanson.vscode-markdownlint.

Here is how my extensions section looks like

"extensions": [
    "davidanson.vscode-markdownlint"
]

With this line in place, the next time we build the container, the extension will be added to Visual Studio Code.

Customization: GitHub token for Jekyll

When building a site to be hosted on GitHub Pages, Jekyll will query GitHub for some information regarding the hosting repository.

If the process is happening locally, Jekyll will complain that no authentication token has been provided with a warning.

GitHub Metadata: No GitHub API authentication could be found. Some fields may be missing or have incorrect data.

To solve this issue, we need to add an environment variable with a valid personal access token.

To begin with, create a new personal access token as explained in this guide and make sure to select the public_repo permission.

Then, let’s create an environment variable on the local machine.

PS> [Environment]::SetEnvironmentVariable("JEKYLL_GITHUB_TOKEN", "<your-new-access-token>", 'User')

The command above will make sure the environment variable will persist after reboots.

Then, let’s modify the devcontainer.json so that the local value is copied into the container.

To do so, we can add the following to the containerEnv section

"containerEnv": {
    "JEKYLL_GITHUB_TOKEN": "${localEnv:JEKYLL_GITHUB_TOKEN}"
}

This will tell Visual Studio Code to use the value of the local variable JEKYLL_GITHUB_TOKEN and copy it into a variable in the container with the same name.

Customization: restore all packages on startup

When working with devcontainers, every time the container starts, it’s in a pristine state.

In the case of a Ruby application like Jekyll, it means that some dependencies specified in the Gemfile might not be available in the container forcing the developer to run a bundle install command after every restart.

Once again, we can leverage the devcontainer.json to specify a list of commands to be executed after the container is created and the source code is mounted.

For this, it’s sufficient to leverage the postCreateCommand section of the file.

"postCreateCommand": [
    "bundle install"
]

Note that this will make starting up a fresh container slower. On the other hand, you will need to execute the command most of the times anyway.

Optional: Sharing git credentials to the container

When working inside a container, it might be use convenient let the container use the same git credentials installed on the local machine.

The Remote-Containers extension has some built-in facilities to make it very smooth the transition into the container. For example, the extension will take care of copying the .gitconfig file in your home directory in the home directory of the container user.

The next step is to configure the SSH agent of the local machine. The Remote-Containers extension will take care of forwarding SSH request from inside the container to the agent running on the local machine.

In an elevated PowerShell console execute the following commands

# Make sure you're running as an Administrator
PS> Set-Service ssh-agent -StartupType Automatic
PS> Start-Service ssh-agent
PS> Get-Service ssh-agent

This will start the ssh-agent service and will make sure it starts automatically on reboot.

Then add the public key to the SSH agent.

PS> cd ~
PS> ssh-add.exe .\.ssh\id_rsa

Once you have added the SSH key to your agent, the container should be able to access and push to your repositories without the need of authenticating at each container reboot.

Recap

In this post we have seen how to configure Visual Studio Code’s extension Remote-Containers to leverage a Docker container and make it easier to work with Jekyll sites on a Windows machine.

In the next post, we will finally see how to create a website with Jekyll, how to install a custom theme and how to customize it.