<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Devcontainers on Haseeb Majid</title>
    <link>https://haseebmajid.dev/tags/devcontainers/</link>
    <description>Recent content in Devcontainers on Haseeb Majid</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Thu, 15 Dec 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://haseebmajid.dev/tags/devcontainers/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to use DotBot to personalise your VSCode Devcontainers</title>
      <link>https://haseebmajid.dev/posts/2022-12-15-how-to-use-dotbot-to-personalise-your-vscode-devcontainers/</link>
      <pubDate>Thu, 15 Dec 2022 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2022-12-15-how-to-use-dotbot-to-personalise-your-vscode-devcontainers/</guid>
      <description>&lt;details
  class=&#34;notice warning&#34;
  open=&#34;true&#34;
&gt;
    &lt;summary class=&#34;notice-title&#34;&gt;Devcontainers&lt;/summary&gt;
  
  This article assumes you are already familiar with dev containers.
You can read more about &lt;a href=&#34;https://code.visualstudio.com/docs/devcontainers/containers&#34;&gt;devcontainers here&lt;/a&gt;.
&lt;/details&gt;

&lt;p&gt;&lt;img
        loading=&#34;lazy&#34;
        src=&#34;https://haseebmajid.dev/posts/2022-12-15-how-to-use-dotbot-to-personalise-your-vscode-devcontainers/images/say-docker.jpeg&#34;
        type=&#34;&#34;
        alt=&#34;Docker Meme&#34;
        
      /&gt;&lt;/p&gt;
&lt;p&gt;In this article, we will go over how you can personalise your dev containers. Devcontainers allow us to create consistent development environments. One of the main advantages of dev containers is we can provide a &amp;ldquo;one button&amp;rdquo; setup for new developers.
We do this by using a container (Docker), and we end up developing inside a container. Much like if we used &lt;code&gt;docker exec -it ubuntu /bin/bash&lt;/code&gt;.
Except it provides a few nice conveniences such as copying (into the container) over the project files and our ssh keys.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<details
  class="notice warning"
  open="true"
>
    <summary class="notice-title">Devcontainers</summary>
  
  This article assumes you are already familiar with dev containers.
You can read more about <a href="https://code.visualstudio.com/docs/devcontainers/containers">devcontainers here</a>.
</details>

<p><img
        loading="lazy"
        src="/posts/2022-12-15-how-to-use-dotbot-to-personalise-your-vscode-devcontainers/images/say-docker.jpeg"
        type=""
        alt="Docker Meme"
        
      /></p>
<p>In this article, we will go over how you can personalise your dev containers. Devcontainers allow us to create consistent development environments. One of the main advantages of dev containers is we can provide a &ldquo;one button&rdquo; setup for new developers.
We do this by using a container (Docker), and we end up developing inside a container. Much like if we used <code>docker exec -it ubuntu /bin/bash</code>.
Except it provides a few nice conveniences such as copying (into the container) over the project files and our ssh keys.</p>
<p>However one of the issues that can arise from this is how you get your dev tools/programs in the dev container.
For example, I use fish shell but lots of Docker containers default to using bash. I also don&rsquo;t want to pollute the Docker file
with a bunch of my specific dev tools. If every developer does that you could end up with a very large Docker file.
This will also mean it takes longer for the dev container to build.</p>
<p>One way we can do this is by using DotBot and a dotfiles repo. I will assume you are familiar with everything we&rsquo;ve covered up to this point.
You have a dotfiles repo which uses DotBot, has profiles and has plugins installed. In this example, we will be using the <a href="https://github.com/bryant1410/dotbot-apt"><code>dotbot-apt</code> plugin</a>.</p>
<h2 id="dotfiles">Dotfiles</h2>
<p>Let&rsquo;s go to our dotfiles repo which we will assume looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">├── ....
</span></span><span class="line"><span class="cl">├── bashrc
</span></span><span class="line"><span class="cl">├── fish
</span></span><span class="line"><span class="cl">│   └── fish.config
</span></span><span class="line"><span class="cl">├── .gitconfig
</span></span><span class="line"><span class="cl">├── install-profile
</span></span><span class="line"><span class="cl">├── install-standalone
</span></span><span class="line"><span class="cl">├── meta
</span></span><span class="line"><span class="cl">│   ├── configs
</span></span><span class="line"><span class="cl">│   │   └── git.yaml
</span></span><span class="line"><span class="cl">│   ├── dotbot
</span></span><span class="line"><span class="cl">│   ├── dotbot-apt
</span></span><span class="line"><span class="cl">│   ├── base.yaml
</span></span><span class="line"><span class="cl">│   └── profiles
</span></span><span class="line"><span class="cl">│       └── linux
</span></span><span class="line"><span class="cl">└── vscode
</span></span></code></pre></div><h3 id="configs">configs</h3>
<p>It may look something like the above. Let&rsquo;s create some new configs specific to our dev container. In this case, we will assume all the
dev containers we will use will be Debian based. So let&rsquo;s create a file <code>meta/configs/packages.debian.yaml</code> which may look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">apt</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">jq</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">fzf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">vim</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">make</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">zoxide</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">exa</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">fish</span><span class="w">
</span></span></span></code></pre></div><p>This will be used to install the specific dev tools I need such as <code>jq</code> and <code>fzf</code>. Next, I want to make sure my fish shell config also gets set up
correctly so we will create another file called <code>meta/configs/shell.yaml</code> which looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">link</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">~/.config/fish</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">fish/**</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">glob</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">create</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></div><h3 id="profiles">profiles</h3>
<p>This will copy (symlink) all of my fish config files to <code>~/.config/fish/</code> directory in the dev container from the dotfiles repo.
Next, let us create a new profile <code>meta/profiles/devcontainer</code> which will look like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">packages.debian-sudo
</span></span><span class="line"><span class="cl">shell
</span></span></code></pre></div><p>Remember by appending <code>-sudo</code> to <code>packages.debian</code> we will run those directives as root i.e. <code>apt</code>.</p>
<h3 id="install-script">Install Script</h3>
<p>So what we have done is create a new profile which will install some of the dev tools we need and copy over our fish config.
Now we have to do one final thing create a new file at the root called <code>install.devcontainer.sh</code> (you can call this whatever
you want, just remember the name). This file looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">./install-profile devcontainer
</span></span></code></pre></div><p>The reason we need this we need to provide an executable file in our VS Code config. We couldn&rsquo;t just run specify this
<code>./install-profile devcontainer</code>. We will see this a bit later.</p>
<h3 id="structure">Structure</h3>
<p>Our repo structure now looks like</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">├── ....
</span></span><span class="line"><span class="cl">├── bashrc
</span></span><span class="line"><span class="cl">├── fish
</span></span><span class="line"><span class="cl">│   └── fish.config
</span></span><span class="line"><span class="cl">├── .gitconfig
</span></span><span class="line"><span class="cl">├── install-profile
</span></span><span class="line"><span class="cl">├── install-standalone
</span></span><span class="line"><span class="cl">├── meta
</span></span><span class="line"><span class="cl">│   ├── configs
</span></span><span class="line"><span class="cl">│   │   ├── shell.yaml
</span></span><span class="line"><span class="cl">│   │   ├── packages.debian.yaml
</span></span><span class="line"><span class="cl">│   │   └── git.yaml
</span></span><span class="line"><span class="cl">│   ├── dotbot
</span></span><span class="line"><span class="cl">│   ├── dotbot-apt
</span></span><span class="line"><span class="cl">│   ├── base.yaml
</span></span><span class="line"><span class="cl">│   └── profiles
</span></span><span class="line"><span class="cl">│       └── linux
</span></span><span class="line"><span class="cl">└── vscode
</span></span></code></pre></div><p>Now let&rsquo;s move on to the repository that is using dev containers. We are going to use a super simple example,
just to demonstrate. Let&rsquo;s create a new file <code>.devcontainer/devcontainer.json</code> which looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Go&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;image&#34;</span><span class="p">:</span> <span class="s2">&#34;mcr.microsoft.com/devcontainers/go:0-1.18&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;features&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;ghcr.io/devcontainers/features/node:1&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;lts&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Configure tool-specific properties.
</span></span></span><span class="line"><span class="cl">  <span class="nt">&#34;customizations&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Configure properties specific to VS Code.
</span></span></span><span class="line"><span class="cl">    <span class="nt">&#34;vscode&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// Set *default* container specific settings.json values on container create.
</span></span></span><span class="line"><span class="cl">      <span class="nt">&#34;settings&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;go.toolsManagement.checkForUpdates&#34;</span><span class="p">:</span> <span class="s2">&#34;local&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;go.useLanguageServer&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;go.gopath&#34;</span><span class="p">:</span> <span class="s2">&#34;/go&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Use &#39;forwardPorts&#39; to make a list of ports inside the container available locally.
</span></span></span><span class="line"><span class="cl">  <span class="c1">// &#34;forwardPorts&#34;: [],
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Use &#39;postCreateCommand&#39; to run commands after the container is created.
</span></span></span><span class="line"><span class="cl">  <span class="c1">// &#34;postCreateCommand&#34;: &#34;go version&#34;,
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
</span></span></span><span class="line"><span class="cl">  <span class="nt">&#34;remoteUser&#34;</span><span class="p">:</span> <span class="s2">&#34;vscode&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This is the default one generated by VS Code for Golang projects (when using the command palette). Normally we would have a custom Docker image
we are using. Perhaps in a future post, I will go over how to use dev containers with an existing custom Docker image. But for this example,
we will just use the Microsoft provided golang image <code>mcr.microsoft.com/devcontainers/go:0-1.18</code>.</p>
<details
  class="notice warning"
  open="true"
>
    <summary class="notice-title">Extension</summary>
  
  You need to have the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers">devcontainer extension</a> installed in VS Code.
</details>

<p>This is enough to create a dev container, we can open the command palette on VS Code and run <code>rebuild and reopen in container</code>. However
this is one final thing we need to do.</p>
<p>Open your <code>settings.json</code> file and add something like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">  <span class="nt">&#34;dotfiles.repository&#34;</span><span class="p">:</span> <span class="s2">&#34;hmajid2301/dotfiles&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;dotfiles.targetPath&#34;</span><span class="p">:</span> <span class="s2">&#34;~/dotfiles&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;dotfiles.installCommand&#34;</span><span class="p">:</span> <span class="s2">&#34;~/dotfiles/install.devcontainer.sh&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li><code>dotfiles.repository</code>: You will need to update the repo <code>hmajid2301/dotfiles</code> to point to your dotfiles repo and it must be accessible on github.</li>
<li><code>dotfiles.targetPath</code>: The <code>targetPath</code> is where in the devcontainer we will git clone our dotfiles repo.</li>
<li><code>dotfiles.installCommand</code>: The executable it will run after the devcontainer is set up. If you called it something else you will need to update that here as well.</li>
</ul>
<p>That&rsquo;s it, now we can have a common dev container set up and personalise with our dotfiles and specific dev tools we want using DotBot.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/dotfiles/-/tree/6b83e990861654506e8ecc756af75cf431438a4a">My Dotfiles</a></li>
<li><a href="https://gitlab.com/hmajid2301/dotfiles/-/blob/77ee6056ae1a1b4ad066348e2b6a3dd6109a409a/meta/profiles/devcontainer">My devcontainer DotBot profile</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
