Dev Environment as a Code (DEaaC) with DevContainers, Dotfiles, and GitHub Codespaces
Tags: productivity, azure, dotnet, github
Published: 2022-08-14 07:15:44

---
title: Dev Environment as a Code (DEaaC) with DevContainers, Dotfiles, and GitHub Codespaces
published: true
date: 2022-08-13 00:00:00 UTC
tags: productivity, azure, dotnet, github
canonical_url: https://nikiforovall.github.io/productivity/devcontainers/2022/08/13/deaac.html
---

### Table of Contents

- [Motivation](#motivation)
- [Anatomy of a dev environment](#anatomy-of-a-dev-environment)
  - [Dotfiles](#dotfiles)
  - [DevContainers](#devcontainers)
- [A better way to do dotfiles - Dotbot](#a-better-way-to-do-dotfiles---dotbot)
- [Combining everything together](#combining-everything-together)
- [Pick a host. GitHub Codespaces](#pick-a-host-github-codespaces)
- [Example](#example)
- [Summary](#summary)
- [Reference](#reference)

## Motivation

Crafting a perfect dev environment takes time and effort. It is a tedious process that you have to do every time you change a working environment. For example, you buy a new laptop, switch to another operating system, or switch to a new project that requires extra tools and configuration. The latter case is interesting to me because I like to try out new things. Varying from different tools, and frameworks to new programming languages.

There are lots of benefits to making a reproducible dev environment. For me the two main ones:

1. You open a project and it just works. Image some open-source project that interests you but you don’t know how to even run it. With DEaaC the installation problems go away.
2. You can share it with your peers. A codified developer setup is a form of knowledge that could be evaluated and evolved. It opens up tons of new opportunities.

## Anatomy of a dev environment

Basically, you can divide aspects of a dev environment into next categories:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gba3mb0ci9ymudkojx6.png)

It might seem like a lot but luckily we have good tools to automate the scaffolding process. Namely **dotfiles** and **devcontainers**.

### Dotfiles

[Dotfiles](https://dotfiles.github.io/) are focused on personal configuration aspects and are usually maintained by a person. Dotfiles are tailored to make up a unique developer experience. Previously, I wrote about it here: [Development environment based on Windows Terminal + WSL + ZSH + dotfiles](https://nikiforovall.github.io/productivity/2019/11/30/nikiforovall-setup.html)

Usually, dotfiles are comprised of common configurations and utilities that could be used on various platforms and platform-specific configurations. For example, you use a specific package manager innate into the system to install dependencies.

Here is an example of how I do it: [https://github.com/NikiforovAll/dotfiles](https://github.com/NikiforovAll/dotfiles). As you can see, there are three different setups for `windows`, `wsl`, and `dev-containers`.

As result, we can create a dev environment of choice from a scratch. All you need to do is to run a one-liner:

```
bash -c "$(wget -qO - https://raw.github.com/nikiforovall/dotfiles/master/src/wsl/os/install.sh)"

```

An approach like this does work. But it is annoying and impractical. We can do better about it. I will show you in a moment.

### DevContainers

From the official documentation:

> The Visual Studio Code Remote - Containers extension lets you use a Docker container as a full-featured development environment. It allows you to open any folder or repository inside a container and take advantage of Visual Studio Code’s full feature set. A devcontainer.json file in your project tells VS Code how to access (or create) a development container with a well-defined tool and runtime stack. This container can be used to run an application or to separate tools, libraries, or runtimes needed for working with a codebase.

> A devcontainer.json file in your project tells Visual Studio Code (and other services and tools that support the format) how to access (or create) a development container with a well-defined tool and runtime stack. It’s currently supported by the [Remote - Containers](https://aka.ms/vscode-remote/download/containers) extension and [GitHub Codespaces](https://github.com/features/codespaces).

<center>
    <img src="https://nikiforovall.github.io/assets/deaac/remote-arch.png" alt="remote-arch">
</center>

DevContainers are meant to be reusable and extendable. From my experience, they have improved in this regard. Here is how you can add dependencies via `.devconatiner.json`

```
{
    "name": "Azure Dev CLI",
    "build": {
        "dockerfile": "Dockerfile", // base docker image
        "args": {
            "VARIANT": "bullseye" // parametrization
        }
    },
    "features": {
        "github-cli": "2",
        "azure-cli": "2.38",
        "dotnet": "6.0",
        "docker-from-docker": "20.10",
    },
    "extensions": [
        "ms-azuretools.azure-dev",
        "ms-azuretools.vscode-bicep",
        "ms-azuretools.vscode-docker",
        "ms-azuretools.vscode-azurefunctions",
        "ms-dotnettools.csharp",
        "ms-dotnettools.vscode-dotnet-runtime",
    ],
}

```

💡DevContainers define project-specific configurations and dotfiles define user-specific configurations.

## A better way to do dotfiles - Dotbot

💭 My initial version of dotfiles was intended for wsl & early devcontainers version. Since then, I’ve learned a couple of lessons. Mainly, it is a good idea to keep dotfiles simple and rely on essentials. You don’t need to bring tons of dependencies and convoluted configuration files. The simpler - the easier to maintain and synchronize.

So, I’ve created another dotfiles repo: [https://github.com/NikiforovAll/dotbot](https://github.com/NikiforovAll/dotbot). This one is based on [https://github.com/anishathalye/dotbot](https://github.com/anishathalye/dotbot). Dotbot is a tool that bootstraps your dotfiles. It is straightforward to use. I suggest you give it a try on your own.

As discussed, since I deliberately choose to not have heavy dependencies it takes a couple of seconds to install dotfiles. Exactly as I want it to be. My general suggestion is to move the heavy lifting of dependencies installation to DevContainers.

One-liner: `cd ~ && git clone https://github.com/NikiforovAll/dotbot && cd ./dotbot && cd ./install`

Here is what the project structure looks like:

```
# oleksii_nikiforov in ~/dotbot
> tree -L 2
.
├── bash
│   ├── plugins.bash
│   └── prompt.bash
├── bash_profile
├── bashrc
├── gitconfig
├── gitignore_global
├── install
├── install.conf.yaml
├── pre-install.sh
├── shell
│   ├── aliases.sh
│   ├── bootstrap.sh
│   ├── dircolors.extra
│   ├── external.sh
│   ├── functions.sh
│   └── plugins
├── zsh
│   ├── plugins
│   ├── plugins_after.zsh
│   ├── plugins_before.zsh
│   ├── prompt.zsh
│   └── settings.zsh
└── zshrc

```

## Combining everything together

The resulting dev environment is combined from diverse sources. Roughly speaking a host (developer machine/codespace) is composed of the next pieces:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vonvbglim0du9ei9d8ha.png)

## Pick a host. GitHub Codespaces

The suggested approach abstracts away the host. It doesn’t necessarily have to be your machine. This is what “GitHub Codespaces” product is about.

> A codespace is a development environment that’s hosted in the cloud. You can customize your project for GitHub Codespaces by committing [configuration files](https://docs.github.com/en/codespaces/customizing-your-codespace/configuring-codespaces-for-your-project) to your repository (often known as Configuration-as-Code), which creates a repeatable codespace configuration for all users of your project.

> GitHub Codespaces run on a variety of VM-based compute options hosted by GitHub.com, which you can configure from 2 core machines up to 32 core machines. You can connect to your codespaces from the browser or locally using Visual Studio Code.

<center>
    <img src="https://nikiforovall.github.io/assets/deaac/codespaces-diagram.png" alt="codespaces-diagram">
</center>
## Example

We will examine Azure Developer CLI (`azd`) template - _todo-csharp-cosmos-sql_. Here is a repo that will be used as an example: [https://github.com/NikiforovAll/todo-csharp-cosmos-sql](https://github.com/NikiforovAll/todo-csharp-cosmos-sql).

The end goal is to set up a full-featured dev environment and measure how much time it takes.

This application utilizes the following Azure resources:

- **Azure App Services** to host the Web frontend and API backend
- **Azure Cosmos DB SQL API** for storage
- **Azure Monitor** for monitoring and logging
- **Azure Key Vault** for securing secrets

The demo combines techniques from both Infrastructure as a Code (IaaC) and Dev Environment as a Code (DEaaC). It takes only **15** staggering minutes from zero to deployed azure resources on the first launch.

1️⃣ Configure dotfiles in “Settings>Codespaces”:

![configure-dotfiles](https://nikiforovall.github.io/assets/deaac/configure-dotfiles.png)

2️⃣ Go to repository and launch a codespace:

![launch-codespace](https://nikiforovall.github.io/assets/deaac/launch-codespace.png)

3️⃣ Open the codespace in the browser or locally (I will go with browser option for the demo)

![codespace-welcome](https://nikiforovall.github.io/assets/deaac/codespace-welcome.png)

4️⃣ Login to Azure via CLI or VSCode extension:

```
az login --use-device-code
```

5️⃣ Run `azd up`

![azd-up](https://nikiforovall.github.io/assets/deaac/azd-up.png)

6️⃣ Setup the breakpoint

![debug-in-codespace](https://nikiforovall.github.io/assets/deaac/debug-in-codespace.png)

7️⃣ Inspect forwarded ports. Note, codespaces allocates some random URL

![port-forwarding-panel](https://nikiforovall.github.io/assets/deaac/port-forwarding-panel.png)

8️⃣ Generate load

![forwarded-port](https://nikiforovall.github.io/assets/deaac/forwarded-port.png)

9️⃣ Inspect logs by running `azd monitor --logs`

🔟 Tear down the dev environment in Azure `azd down`

![azd-down](https://nikiforovall.github.io/assets/deaac/azd-down.png)

As result we have:

- Deployed whole developer setup in **15** minutes
- Terminal/Shell is configured
- Git is configured
- All project tooling and dependencies are installed: `dotnet`, `az`, etc.
- VSCode contains user extensions as well as suggested by devcontainer
- One happy developer 😉

## Summary

DEaaS has lots of advantages. Most of the time it just works. But it definitely required investing some time to learn about the moving parts to know how to maintain and troubleshoot the setup. I have not shown you the dark side of using DevContainers but I can assure you that it could be hard to understand when something goes wrong. Despite all challenges, it opens new horizons and it is a future of large-scale and open-source development.

## Reference

- [https://dotfiles.github.io](https://dotfiles.github.io)
- [https://nikiforovall.github.io/productivity/2019/11/30/nikiforovall-setup.html](https://nikiforovall.github.io/productivity/2019/11/30/nikiforovall-setup.html)
- [https://code.visualstudio.com/docs/remote/create-dev-container](https://code.visualstudio.com/docs/remote/create-dev-container)
- [https://docs.github.com/en/codespaces/overview](https://docs.github.com/en/codespaces/overview)
- [https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/introduction-to-dev-containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/introduction-to-dev-containers)
- [https://docs.github.com/en/codespaces/customizing-your-codespace/personalizing-github-codespaces-for-your-account](https://docs.github.com/en/codespaces/customizing-your-codespace/personalizing-github-codespaces-for-your-account)