Blog

Customizing Codespaces

19 Apr, 2022
Xebia Background Header Wave

You’ve probably had this situation at least once in your career: you join a new team and it takes you at least 10 days to finally get the build to succeed on your local machine, the tests to pass, the application to launch without issues, and for the debugger to work. There’s a document somewhere or in the projects wiki with a lot of steps, and the last person who walked through it did so 9 months ago. Situations like this cost precious time and are a big source of frustration.

Some companies solve this by having you work on a Virtual Machine, either locally on Hyper-V or remotely in a data center or the cloud. This solves quite a few problems, but the cost is often prohibitive, and I’ve personally never liked having to work inside a remote desktop, often on a machine that was shared with others, while my own desktop has twice the power.

GitHub Codespaces provides a solution for many of these issues.

What is Codespaces

For most people, Codespaces can be described as Visual Studio Code in the browser. Yet, it’s much more. It’s a cloud-based container platform for developers to run their complete development environment.

When you launch Codespaces from an enabled repository or organization, by default it launches a version of Visual Studio Code with all the latest developer tools pre-installed for just about every popular programming language. And developers can write their code, run their tests, and even run and debug their application, inside the browser!

And while Codespaces runs in the cloud, your editor “runs” inside of your browser or inside of a local instance of Visual Studio Code.

Code Spaces are hosted in Azure and Visual Studio Code uses Remote Containers[1] to connect. All changes made to the Codespace’s filesystem are automatically captured. Even when your Codespace is paused, it will resume right where you left off.

Interesting use-cases

In the past few months, we have used Codespaces to deliver online interactive workshops where participants could get started with new technology and tools they had never used before without installing anything to their local laptops. This greatly simplified the preparations for the workshop and completely took away the need for pre-provisioned workstations.

We’ve configured Codespaces for internal projects so that all it takes for a developer to contribute to the project is to start the Codespace and wait a few seconds for the Codespace to start. From this point forward, they can change the code, run the tests, and run a local instance without having to configure anything locally and without any interference with any of their ongoing projects.

I’m personally considering adding a Codespace configuration to most of my open-source projects to make it much easier for people to contribute.

We plan to leverage Codespaces for the upcoming Global DevOps Bootcamp[2] so that every participant has access to a fast and pre-configured IDE in the cloud regardless of their own hardware and circumstances, hopefully enabling many more people to participate in the event.

Getting Started

To start using Codespaces, you don’t need to know how to create your own image. There is a large list of starter containers available, and the default container has tools for just about every popular programming language pre-installed. Just click the “New Codespace” button in your repository to open the repository in a new instance of Visual Studio Code inside your browser.

Figure Figure 1. Creating a new Codespace

While the default image is convenient, it’s also a bit big and probably has many tools installed you’re unlikely to ever use. To pick one of the other available images, choose the “Add Development Container Configuration Files…” from the command palette.

Figure Figure 2 Add Development Container Configuration Files

And choose the container image you want to use. When in doubt, pick the “GitHub Codespaces (Default)”. A complete overview of all the images and what’s installed on them can be found on GitHub[3].

Figure Figure 3 Choose the container image matching your environment

Visual Studio Code will add several files to your repository and then prompts you to rebuild the Codespace:

Figure

Note: In case you missed the prompt, you can always manually trigger a rebuild from the command palette (Ctrl+Shift+P). This can also be useful when you want to make multiple changes and then rebuild the Codespace.

You’ll see a new folder in your repository containing these new files: .devcontainer/devcontainer.json and .devcontainer/DockerFile. These files are used to store most of the settings of your Codespace.

Figure

Anatomy of a Codespace

The configuration of your Codespace is stored in several places. You’ve already seen the first two in the .devcontainer folder. But there are more. Let’s go over them to see what they are:

.devcontainer/devcontainer.json

The devcontainer.json is the main configuration file for your Codespace. It contains environment variables, extensions, docker volume mounts and a few other settings. It also points to the container image used to run your development container. The default points to the DockerFile in the same directory, but you can also reference any image from a docker repository of your choice.

The devcontainer.json can also be used to run one of many commands after Visual Studio Code has launched. At this point, your git repository contents will also be available.

.devcontainer/DockerFile

The DockerFile is used to select the base image and to optionally install additional tools into your container. By default, it’s a simple pointer to the image you selected when you had Visual Studio Code add the Development Container Configuration Files to your repository.

Your GitHub profile

Additional settings, such as themes, keyboard bindings, snippets and globally installed extensions can be synced with your GitHub profile into your Codespace by turning on Settings Sync[4].

Figure

Codespaces will ask what settings to synchronize and will ask what to do in case there are conflicting settings:

Figure

Some customizations, like keyboard bindings, can only be configured through Settings Sync or through Visual Studio Code extensions.

Your dotfiles repository

In your personal GitHub settings, you can configure a repository containing your Linux dotfiles[5]. These can be used to configure your default shell, your preferred editor, and many other settings of your Linux user profile.

Figure

Codespaces Secrets

You may need to access other resources from your Codespace, such as a GitHub Container Registry, Cloud resources etc. To prevent accidentally committing these secrets to your repository it’s recommended to not store these credentials on the filesystem. Instead, store them in Codespaces Secrets. When a Codespace starts, there secrets are made available as environment variables.

Secrets can be stored on multiple levels:

  • Repository (most specific)
  • User Settings
  • Organization Settings (least specific)

The most specific level will be used by your Codespace.

Note: Whenever a secret is updated, you must rebuild your Codespace for these changes to take effect. Unfortunately, there is no indication this is required from inside your Codespace.

Note: You can’t store secrets with a key that starts with GITHUB_. Which is unfortunate since some tools expect that. In that case, you’ll need to copy the value from a different name to the reserved name after the Codespace has started.

There are special secrets to allow access to private docker repositories[6]. These must be named:

  • ***_CONTAINER_REGISTRY_PASSWORD
  • ***_CONTAINER_REGISTRY_SERVER
  • ***_CONTAINER_REGISTRY_USERNAME

Where *** is a custom label to identify the container registry.

Common scenarios

The most common reason to customize your Codespace is probably the need to install additional tools that are required for your development process or changing the set of installed extensions. Every time you make changes, you can immediately test them by rebuilding your Codespace. When you are satisfied with your changes, commit your changes to the repository to persist them and to share them with the world.

Installing additional tools

While the default Codespace container has many things installed, you may need to add something extra to it. Either a custom-built tool, or something that requires a license to run.

You can add these by editing the DockerFile in the .devcontainer folder.


FROM mcr.microsoft.com/vscode/devcontainers/universal:1-focal

USER root
RUN apt-get update

USER Codespace
RUN az extension add --name azure-devops

You can run commands at the container level (USER root) or at the user level (USER Codespace).

Adding extensions

The list of extensions to install is stored in the .devcontainer.json. You can manually add extensions to the list and then rebuild then your Codespace.

Figure Figure 4 Manually add an extension to the devcontainer.json

But there is an easier way to achieve this. When you’re inside your Codespace, you can add the extension from the Extensions Marketplace:

Figure Figure 5 Add an extension through the Extension Marketplace

Find the extension you need, then add it to the .devcontainer.json from the cogwheel menu.

Caching containers inside the Codespace

One of the great advantages of Codespaces is that you can get started on a project quickly with the click of a button. Once the Codespace has started, you can pull additional images, so they’re cached locally:


{

"postCreateCommand": "<strong>docker pull ghcr.io/jessehouwing/mycustom-cli:latest</strong> && ... "

}

To pull the image from a private repository add the previously mentioned ***_CONTAINER_REGISTRY secrets.

Storing the Codespace container in GitHub Container Registry

It may not be desirable to build your container from scratch each time it’s started up and you may not want to store the container in a publicly accessible location. In that case, you can store your container in GitHub Container Registry and grant access to Codespaces.

First build and tag your container image:


> docker build .
[+] Building 0.2s (6/6) FINISHED
=> [internal] load build definition from Dockerfile                                                               0.1s
=> => transferring dockerfile: 162B                                                                               0.0s
=> [internal] load .dockerignore                                                                                  0.1s
=> => transferring context: 2B                                                                                    0.0s
=> [internal] load metadata for mcr.microsoft.com/vscode/devcontainers/universal:1-linux                          0.0s
=> [1/2] FROM mcr.microsoft.com/vscode/devcontainers/universal:1-linux                                            0.0s
=> CACHED [2/2] RUN az extension add --name azure-devops                                                          0.0s
=> exporting to image                                                                                             0.1s
=> => exporting layers                                                                                            0.0s
=> => writing image sha256:aa1d12f58610a60d4ee53b7dfc06b2b5a9581f5e26de19931deb61c3b66b120f                       0.0s

Tag and publish the image to GitHub Container Registry:


> docker tag aa1d12f58610a60d4ee53b7dfc06b2b5a9581f5e26de19931deb61c3b66b120f ghcr.io/jessehouwing/Codespaces-demo:latest
> docker push ghcr.io/jessehouwing/Codespaces-demo:latest
The push refers to repository [ghcr.io/jessehouwing/Codespaces-demo]
……
latest: digest: sha256:d928fbe90f267882d4d4de4194015eef06f5c88a045f3d9d4334aae0ea104612 size: 4538

Then navigate to the package settings for the container image you just pushed and grant access to GitHub Codespaces:

Figure Figure 6 Find the newly published Codespace container and open the Package Settings

Grant the repository you want to launch this Codespace image from access to this package:

Figure Figure 7 Manage Codespace access to add the repository

Now update the DockerFile in the repository to use this image:


FROM ghcr.io/jessehouwing/Codespaces-demo:latest

And rebuild your Codespace.

Beyond extending the base image

Your requirements for the Codespace image may go beyond the standard images, maybe you need a different Linux distro, standard libraries, a specific kernel version etc. In that case, you can also build a Codespace from scratch. A nice getting started point could be to take the Codespaces default container[7] and either re-use the elements you need or use them as inspiration for your own image.

To start customizing the image, copy the contents of the .devcontainer folder of one of the standard images and replace the DockerFile with the base.DockerFile. You’ll find all the scripts used to install the different toolsets in the library-scripts folder.

Either commit the .devcontainer folder and its contents directly to your repository or build the container and publish it to a container registry as described above.

Figure Figure 8 Take the full contents of a Codespace image to customize it even further

How CodeSpaces will change the way developers work

It is hard to find decent laptops and other hardware these days. The lack of available chips does not make it any easier to get your hands on some proper hardware. Even when you have the hardware, it can be a pain to onboard new team members, not to mention the external consultants who bring their own devices. What if there would be a solution for all of this? There is one, and it is called GitHub CodeSpaces. With GitHub CodeSpaces, your editor runs in a browser. You do not need to buy expensive hardware every few years to run all the latest developer’s tools. Even one ultra-light laptop or tablet is enough to access CodeSpaces.

CodeSpaces uses docker images to run open-source tools like VScode. These default images contain most of the common compilers and tools you may need. "But I need this really specific tool for my project". Luckily you can create your custom CodeSpaces images and add your own tools to your heart’s content. You can start with a default image and let your developers add their extensions. That is the best of both worlds, you are in control and rapidly spin up a fresh new image, and the developer can customize these further if needed.

There is one more advantage for CodeSpaces. With an average development laptop, the source code is on the laptop and travels with your developer anywhere they go. It may not be the latest version, but still. With CodeSpaces, your code does not need to be on the laptop, it stays on GitHub. This makes CodeSpaces more secure.

GitHub CodeSpaces will be the next big thing in development tooling and it is going to change the way we work as developers.

Summary

Codespaces enables teams worldwide to contribute to GitHub. It drastically reduces the time needed for anyone to open a project and contribute their changes.

Even when the standard options won’t fulfill your needs, it’s easy to extend and change what is installed and updates can be rolled out to your team effortlessly.

Are you passionate about DevOps?

At Xpirit we live and breathe DevOps. Since our start, it has been at the heart of our organization and this goes beyond the tools. But tools also help you build a great engineering culture and the tools we love and embrace are Azure DevOps and GitHub. GitHub has been at the front and center of Open Source and is being adopted by many enterprises. Since Microsoft acquired GitHub we became the first GitHub Verified Partner in Europe! Our team of DevOps experts also got multiple GitHub accreditations to deliver services and training as part of the GitHub extended technology services team! We even train Microsoft employees on how to use GitHub!

Sounds fun to be part of this team? Do you want to work in an environment that pushes you to learn more every day? Do you want to become part of this team of DevOps experts? Do you like complex problems, want to be challenged, and have a supporting team of colleagues that help you move your capability needle to the max? Let’s have a chat and explore if Xpirit can support you in your career journey and do epic assignments at our world-class customers. We love to explore the opportunities together. Let’s have a cup of coffee or tea and a chat about your future. Not into coffee or tea, perhaps even better: let’s have a beer! The first step to your dream job is by taking action, !

[1] Developing inside a Container using Visual Studio Code Remote Development

[2] globaldevopsbootcamp.com/

[3] vscode-dev-containers/containers at main · microsoft/vscode-dev-containers · GitHub

[4] Settings Sync

[5] dotfiles

[6] Allowing your codespace to access a private registry

[7] .devcontainer

Jesse Houwing
Jesse is a passionate trainer and coach, helping teams improve their productivity and quality all while trying to keep work fun. He is a Professional Scrum Trainer (PST) through Scrum.org, Microsoft Certified Trainer and GitHub Accredited Trainer. Jesse regularly blogs and you'll find him on StackOverflow, he has received the Microsoft Community Contributor Award three years in a row and has been awarded the Microsoft Most Valuable Professional award since 2015. He loves espresso and dark chocolate, travels a lot and takes photos everywhere he goes.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts