<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cheng Shao</title>
    <description>The latest articles on DEV Community by Cheng Shao (@terrorjack).</description>
    <link>https://dev.to/terrorjack</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F832303%2F03664b47-40de-4344-8e7d-f8f26a492250.png</url>
      <title>DEV Community: Cheng Shao</title>
      <link>https://dev.to/terrorjack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/terrorjack"/>
    <language>en</language>
    <item>
      <title>Gitpod notes and thoughts</title>
      <dc:creator>Cheng Shao</dc:creator>
      <pubDate>Sat, 30 Jul 2022 04:48:12 +0000</pubDate>
      <link>https://dev.to/terrorjack/gitpod-notes-and-thoughts-3ged</link>
      <guid>https://dev.to/terrorjack/gitpod-notes-and-thoughts-3ged</guid>
      <description>&lt;p&gt;A few months ago, a colleague reposted a Gitpod &lt;a href="https://www.gitpod.io/blog/gitpod-for-opensource"&gt;blog post&lt;/a&gt; in my employer's slack, mentioning that if you're a member of some reputable GitHub organization, you get a plan with unlimited hours. Thank you, the past me who spent a weekend packaging an app for nixpkgs!&lt;/p&gt;

&lt;p&gt;I knew these kinds of cloud IDEs have been around for some time, but that message inspired me to give it a bit of try, who can resist free lunch after all? Well, I'm now using it on a daily basis, so this post will record some notes and thoughts along the way. The content is not quite well structured, but I hope they can provide a bit of insight for other Gitpod users as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hacking GHC
&lt;/h2&gt;

&lt;p&gt;The first Gitpod use case I tried is hacking &lt;a href="https://gitlab.haskell.org/ghc/ghc"&gt;GHC&lt;/a&gt;. And that's quite a roadbump because GHC is hosted on their own GitLab instance!&lt;/p&gt;

&lt;p&gt;Yes, you probably can fiddle with tokens and such, to make Gitpod work with self-hosted GitLab. It's mentioned in Gitpod docs, I haven't tried, since there's another problem ahead:&lt;/p&gt;

&lt;p&gt;Gitpod requires you to check in &lt;code&gt;.gitpod.yml&lt;/code&gt; into the repo you're working with. That's reasonable for most single-repo use cases, but I don't have any mental energy to upstream a Gitpod config, nor do I want to pollute my GHC working branches with this config file.&lt;/p&gt;

&lt;p&gt;So I made a dedicated GitHub &lt;a href="https://github.com/TerrorJack/gitpod-ghc"&gt;repo&lt;/a&gt; for hacking GHC. Gitpod allows you to specify scripts to run during workspace initialization, so I can just clone GHC to &lt;code&gt;/workspace&lt;/code&gt; and open it later, profit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git authentication with SSH
&lt;/h2&gt;

&lt;p&gt;All looks good except when I need to push. For GitHub repos I own, pushes to &lt;code&gt;https&lt;/code&gt; remotes should work out of the box, but well, this doesn't work for GHC's self-hosted GitLab.&lt;/p&gt;

&lt;p&gt;This hasn't been a problem when I used my employer's remote build machine, since I can always enable SSH agent forwarding, so I can do the pushes on that machine. Gitpod allows you to SSH into an active workspace, but honestly I don't want to open a separate terminal window just for git operations, I prefer to stay inside that browser tab all the time.&lt;/p&gt;

&lt;p&gt;So. Given Gitpod allows specifying secret environment variables, I generated a passwordless SSH private key and encoded it as a secret. The &lt;code&gt;gitpod-ghc&lt;/code&gt; init script will start an &lt;code&gt;ssh-agent&lt;/code&gt; and automatically add that key, so future &lt;code&gt;git&lt;/code&gt; operations will work out of the box.&lt;/p&gt;

&lt;p&gt;Yes, doesn't sound like a good security story, other than permuting the key frequently and limiting the key's scope, I still need to trust Gitpod not to leak my secret, no less than they trust me not to abuse their machines to run crypto miners.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's persisted and what's not
&lt;/h2&gt;

&lt;p&gt;There are multiple ingredients in making a Gitpod workspace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The base &lt;code&gt;Dockerfile&lt;/code&gt; or a Docker Hub image. Use whatever you like, but given the Gitpod runtime will yolo a bunch of Linux binaries at runtime, it better be a typical glibc-based FHS distro.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.gitpod.yml&lt;/code&gt; config, which allows you to specify init scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having two places to write scripts may seem redundant, but the &lt;code&gt;.gitpod.yml&lt;/code&gt; scripts have access to the &lt;code&gt;/workplace&lt;/code&gt; directory. Across restarts of the same Gitpod workspace, only changes in &lt;code&gt;/workplace&lt;/code&gt; is persisted!&lt;/p&gt;

&lt;p&gt;It's quite an annoyance since for a long-lived workspace, you often accumulate implicit state (e.g. language-specific caches) not only in your project directory, but also in the home directory.&lt;/p&gt;

&lt;p&gt;Luckily, there's a limited workaround for that: bind mounts. In &lt;code&gt;gitpod-ghc&lt;/code&gt; config, I bind mount a lot of home directory contents from &lt;code&gt;/workplace&lt;/code&gt; instead, things like &lt;code&gt;.config&lt;/code&gt;, &lt;code&gt;.cache&lt;/code&gt;, &lt;code&gt;.cabal&lt;/code&gt;. Even the entire Nix store!&lt;/p&gt;

&lt;p&gt;It's a pity I can't bind mount the entire home directory though, likely because of busy resources during workplace initialization. And of course, all system package manager invocations are forgotten after workplace restart.&lt;/p&gt;

&lt;p&gt;To think of it in a more positive way, Gitpod is enforcing the &lt;a href="https://grahamc.com/blog/erase-your-darlings"&gt;erase your darlings&lt;/a&gt; philosophy. It doesn't strictly prohibit state, it forces you to make your state explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prebuilds
&lt;/h2&gt;

&lt;p&gt;Gitpod prebuilds is a CI service that runs on the same infra to power the workspaces. When a prebuild is triggered, the Docker image gets built, a fresh &lt;code&gt;/workplace&lt;/code&gt; with a checkout of that commit is mounted, and the expensive (&lt;code&gt;init&lt;/code&gt;) step of &lt;code&gt;.gitpod.yml&lt;/code&gt; scripts get run. The updated &lt;code&gt;/workplace&lt;/code&gt; is saved and reused upon workplace start up, so the &lt;code&gt;init&lt;/code&gt; step can be skipped at that time.&lt;/p&gt;

&lt;p&gt;For a large project like GHC, enabling Gitpod prebuilds is essential to ensure a sane startup time for new workspaces. Here's a non-comprehensive list of things done during &lt;code&gt;gitpod-ghc&lt;/code&gt; prebuild time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install latest release versions of &lt;code&gt;ghcup&lt;/code&gt;, &lt;code&gt;ghc&lt;/code&gt; and &lt;code&gt;cabal&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Compile and install &lt;code&gt;haskell-language-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check out GHC tree into &lt;code&gt;/workspace&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go through the &lt;code&gt;configure&lt;/code&gt; step, build &lt;code&gt;hadrian&lt;/code&gt;, build up the &lt;code&gt;.hie-bios&lt;/code&gt; cache, so the language server shall work nearly instantly for a fresh workspace&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  In-browser VSCode caveats
&lt;/h2&gt;

&lt;p&gt;Oops. I've written a lot in this post and haven't even mentioned VSCode even once. Yes, Gitpod supports multiple IDEs, but the one I'm using on a daily basis is the in-browser VSCode. Some caveats to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most default key bindings work. ^W closes the browser tab instead of the editor tab though, luckily there'll be a prompt about unsaved work. Don't silence that prompt.&lt;/li&gt;
&lt;li&gt;Flaky connection is fine, but don't drop offline for more than 3 minutes.&lt;/li&gt;
&lt;li&gt;Settings do get synced properly. A minor annoyance is you can't declare editor settings in &lt;code&gt;.gitpod.yml&lt;/code&gt;, apart from the extension list.&lt;/li&gt;
&lt;li&gt;The default extension store is OpenVSX instead of the Microsoft-hosted one. There do exist extensions that are severely outdated in OpenVSX (like &lt;code&gt;rust-analyzer&lt;/code&gt;), but as a fallback measure, you can specify URLs to &lt;code&gt;.vsix&lt;/code&gt; artifacts in &lt;code&gt;.gitpod.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other things known to work (or not)
&lt;/h2&gt;

&lt;p&gt;There are things that work, and things that don't work yet. I didn't really spend time to dig into Gitpod's issue tracker, so I'll be more than happy to be corrected in the comments shall the list requires some update!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sudo&lt;/code&gt; works. Of course you're still in a container jail, so you can't really mess with &lt;code&gt;sysctl&lt;/code&gt; config and such.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;systemd&lt;/code&gt; doesn't work. You need to bring your own userspace supervisor if you need such a thing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nix&lt;/code&gt; works with single-user installation and &lt;code&gt;sandbox = false&lt;/code&gt;. Multi-user mode with &lt;code&gt;nix-daemon&lt;/code&gt; should still be possible, but not worth the effort if sandboxing won't work anyway. So don't ever push to binary caches from a Gitpod workspace.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker&lt;/code&gt; works out of the box as rootless mode, and the state directory is in &lt;code&gt;/workplace&lt;/code&gt;. &lt;code&gt;podman&lt;/code&gt; doesn't, despite unprivileged user namespace is on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gdb&lt;/code&gt; works. &lt;code&gt;strace&lt;/code&gt; works. &lt;code&gt;lldb&lt;/code&gt; doesn't. &lt;code&gt;rr&lt;/code&gt; doesn't.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;proot&lt;/code&gt; latest version works.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tailscale&lt;/code&gt; userspace mode works, but running the daemon as &lt;code&gt;root&lt;/code&gt; seems quite fragile. Haven't tried other VPNs yet.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;htop&lt;/code&gt; kinda works. The CPU/RAM stats don't reflect the workspace, but reflects the entire metal below it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Community support
&lt;/h2&gt;

&lt;p&gt;Gitpod development happens on GitHub, they allow self-hosting and the sausages are made in the open.&lt;/p&gt;

&lt;p&gt;Apart from that, there's an official discord server. Not a super active one, but the staff are there, and questions do get answered within a day or two, regardless of whether you're a paid user or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I believe I've accumulated enough experience to say: Gitpod is nice, and it's worth a try.&lt;/p&gt;

</description>
      <category>gitpod</category>
    </item>
    <item>
      <title>Thoughts about configure scripts and feature vectors</title>
      <dc:creator>Cheng Shao</dc:creator>
      <pubDate>Fri, 06 May 2022 14:17:10 +0000</pubDate>
      <link>https://dev.to/terrorjack/thoughts-about-configure-scripts-and-feature-vectors-2lk6</link>
      <guid>https://dev.to/terrorjack/thoughts-about-configure-scripts-and-feature-vectors-2lk6</guid>
      <description>&lt;p&gt;For a long time, I’ve been wanting to rant about stuff like configure scripts. They indirectly contribute a lot to my worktime headaches these days. Given I’m restarting personal blogging here, let’s see if I can turn that rant into a post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s configure
&lt;/h2&gt;

&lt;p&gt;Suppose you need to compile some software from a source tarball. The old unix tradition goes like &lt;code&gt;./configure &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make install&lt;/code&gt;. Nothing fancy about the &lt;code&gt;make&lt;/code&gt; part, but why is &lt;code&gt;configure&lt;/code&gt; needed in the first place?&lt;/p&gt;

&lt;p&gt;Well, &lt;code&gt;configure&lt;/code&gt; is just a shell script that probes the build environment and generates some C header file to be included in the source code. Each project has its unique &lt;code&gt;configure&lt;/code&gt; script that probes for different things, headers, functions, any feature that the source code needs to check for build-time existence and provide a fallback code path when it’s absent.&lt;/p&gt;

&lt;p&gt;Say that the source code needs to call the &lt;code&gt;foo&lt;/code&gt; function, which exists on just some of the project’s supported platforms. If &lt;code&gt;configure&lt;/code&gt; detects &lt;code&gt;foo&lt;/code&gt;, it’ll write a &lt;code&gt;#define HAVE_FOO 1&lt;/code&gt; line to the generated header. The source code can then include the auto-generated feature header, use CPP declarations like &lt;code&gt;#if defined(HAVE_FOO)&lt;/code&gt; to decide whether function &lt;code&gt;foo&lt;/code&gt; exists in the build environment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;configure&lt;/code&gt; is typically auto-generated from a template using autoconf. In some projects it can be a hand-written python script. There are also build systems like cmake that take over &lt;code&gt;configure&lt;/code&gt;'s role completely, probes the build environment on their own and generate the feature header.&lt;/p&gt;

&lt;p&gt;Anyway, my rants here are only related to the idea of build-time feature detection, and irrelevant to how &lt;code&gt;configure&lt;/code&gt; is actually implemented (although that’s also annoying enough for it’s own blog post)&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s a feature vector
&lt;/h2&gt;

&lt;p&gt;How many &lt;code&gt;HAVE_&lt;/code&gt; macros do you have in your project?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/ubuntu/ghc&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rIF&lt;/span&gt; HAVE_ | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
842
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a sec. Most of those should be mere duplications, for instance, &lt;code&gt;HAVE_FOO&lt;/code&gt; is very likely to occur in multiple source locations. One should really check how many features (headers, functions, etc) are checked by &lt;code&gt;configure&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/ubuntu/ghc&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rIF&lt;/span&gt; AC_CHECK_ | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
171
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The number above is a lower estimation, since in autoconf, a single &lt;code&gt;AC_CHECK_HEADERS&lt;/code&gt; or &lt;code&gt;AC_CHECK_FUNCS&lt;/code&gt; line can check multiple entities.&lt;/p&gt;

&lt;p&gt;Now, we can introduce the concept of a “feature vector”: an &lt;code&gt;N&lt;/code&gt;-dimentional boolean vector, where &lt;code&gt;N&lt;/code&gt; corresponds to the number of things you’re checking at build-time. Each value of the feature vector is a point in the feature space, specifying a build-time configuration.&lt;/p&gt;

&lt;p&gt;How large is the feature space?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Definitely not as large as &lt;code&gt;2^N&lt;/code&gt;. Most of the dimensions aren’t orthogonal, one can imagine clusters of things that either exist as a whole, or not exist at all.&lt;/li&gt;
&lt;li&gt;Still, way larger than the the space where people actually test on CI to avoid bit-rotting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My rant is the second point above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the rant
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In GHC, one can pass various &lt;code&gt;configure&lt;/code&gt; arguments to enable/disable features like unreg codegen, large address space, native IO manager, etc. The default configuration passes the test suite, but once you start messing with &lt;code&gt;configure&lt;/code&gt; config, expect failed test cases. At least those cases should be explicitly marked fragile/broken in those configurations!&lt;/li&gt;
&lt;li&gt;In GHC, &lt;code&gt;unix&lt;/code&gt;, probably other places I’ve hacked and forgotten: the API evolves, but people forgot to update the code in the &lt;code&gt;#else&lt;/code&gt; -guarded parts, because it’s not tested on CI, maybe that particular CPP-checked thing is thought to exist on all platforms. Well, WASI is a rather restricted platform, so those bitrotten parts all come back to bite you when I target WASI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s nothing to blame for the need to write portable code and do build-time feature detection. We all know untested code is bad, but much fewer people are aware: untested feature vectors are also bad!&lt;/p&gt;

&lt;p&gt;Also, this isn’t just a matter of “code coverage”. It’s perfectly possible to achieve a high coverage rate by testing against just a few feature vectors, while leaving the potentially broken build-time configurations in the dark.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to solve it
&lt;/h2&gt;

&lt;p&gt;Most software written with tons of &lt;code&gt;#ifdef&lt;/code&gt; lack the feature vector mindset, and don’t have the testing logic to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perform &lt;code&gt;QuickCheck&lt;/code&gt;-style random testing in the feature space. Generate a feature vector, run the tests against it, and “shrinking” is just moving the point closer to the known-to-work base point, the default config you get when configuring on a typical platform without any custom arguments. This allows discovering failures that arise from complex &amp;amp; unintended interactions between different dimensions in the feature vector.&lt;/li&gt;
&lt;li&gt;Hide certain existing auto-detected features. This allows testing for restricted/exotic platforms, while still running the tests on a common platform. Not trivial to implement, especially for things in standard libraries, but it should be possible by using &lt;code&gt;poison&lt;/code&gt; pragma, creating &lt;code&gt;cc&lt;/code&gt; wrappers, or even isolated sysroots.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>My GHC dev environment with vscode remote &amp; docker</title>
      <dc:creator>Cheng Shao</dc:creator>
      <pubDate>Thu, 28 Apr 2022 15:10:51 +0000</pubDate>
      <link>https://dev.to/terrorjack/my-ghc-dev-environment-with-vscode-remote-docker-3ek4</link>
      <guid>https://dev.to/terrorjack/my-ghc-dev-environment-with-vscode-remote-docker-3ek4</guid>
      <description>&lt;p&gt;For my daily work of hacking GHC, I’ve recently transitioned to a workflow of using VSCode remote + Docker containers. The main advantage is being able to reuse the same Docker images used by GHC CI, so you can have more confidence with local test results before pushing. This post briefly walks through the process to set up such a dev environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building &amp;amp; starting the dev container
&lt;/h2&gt;

&lt;p&gt;The first step is picking an image specified in the GHC CI config, using that as a base image to build our dev image. The original images aren’t suitable to be used directly, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The default &lt;code&gt;ghc&lt;/code&gt; user’s uid/gid likely doesn’t match your host user’s uid/gid. This will result in file permission issues if you mount a host directory into the container and use that as the working directory.&lt;/li&gt;
&lt;li&gt;The default &lt;code&gt;PATH&lt;/code&gt; doesn’t contain &lt;code&gt;ghc&lt;/code&gt;-controlled directories like &lt;code&gt;~/.cabal/bin&lt;/code&gt; or &lt;code&gt;~/.local/bin&lt;/code&gt;. This can be a minor annoyance if you build and install HLS(haskell-language-server) in those directories, the VSCode Haskell extension wouldn’t be able to find the executable automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the &lt;code&gt;Dockerfile&lt;/code&gt; recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-deb10:0849567cd9780cc8e9652118b949cb050c632ef4&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; UID&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; GID&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;  curl &lt;span class="nt"&gt;-SsL&lt;/span&gt; https://github.com/boxboat/fixuid/releases/download/v0.5.1/fixuid-0.5.1-linux-amd64.tar.gz | &lt;span class="nb"&gt;sudo tar&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /usr/local/bin &lt;span class="nt"&gt;-xzf&lt;/span&gt; - &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;sudo chown &lt;/span&gt;root:root /usr/local/bin/fixuid &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;sudo chmod &lt;/span&gt;4755 /usr/local/bin/fixuid &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/fixuid &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"user: ghc&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;group: ghc&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/fixuid/config.yml

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; ${UID}:${GID}&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;fixuid

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; ghc&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    /etc/fixuid &lt;span class="se"&gt;\
&lt;/span&gt;    /usr/local/bin/fixuid &lt;span class="se"&gt;\
&lt;/span&gt;    /var/run/fixuid.ran

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH=${PATH}:/home/ghc/.cabal/bin:/opt/ghc/9.2.2/bin&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    bash-completion &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;cd&lt;/span&gt; /tmp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  git clone https://github.com/haskell/haskell-language-server.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;cd &lt;/span&gt;haskell-language-server &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  cabal &lt;span class="nt"&gt;--project-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cabal-ghc92.project update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  cabal &lt;span class="nt"&gt;--project-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cabal-ghc92.project &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Which base image to use? Check out &lt;a href="https://gitlab.haskell.org/ghc/ghc/-/blob/master/.gitlab-ci.yml"&gt;&lt;code&gt;.gitlab-ci.yml&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://gitlab.haskell.org/ghc/ghc/-/blob/master/.gitlab/jobs.yaml"&gt;&lt;code&gt;jobs.yaml&lt;/code&gt;&lt;/a&gt; to figure out which Docker images are used by GHC CI. Here we use &lt;code&gt;deb10&lt;/code&gt; , although a bit outdated, it’s the most widely used one, especially for edge cases like &lt;code&gt;unreg&lt;/code&gt; or &lt;code&gt;tsan&lt;/code&gt; CI jobs.&lt;/li&gt;
&lt;li&gt;We use the &lt;a href="https://github.com/boxboat/fixuid"&gt;&lt;code&gt;fixuid&lt;/code&gt;&lt;/a&gt; project for correcting the default &lt;code&gt;ghc&lt;/code&gt; user’s uid/gid to the numbers provided. Typically, &lt;code&gt;fixuid&lt;/code&gt; is invoked once upon first run of the container, but it’s actually possible to do this at image build time. The &lt;code&gt;UID&lt;/code&gt; and &lt;code&gt;GID&lt;/code&gt; variables must be specified to &lt;code&gt;docker build&lt;/code&gt; via &lt;code&gt;--build-arg UID=$(id -u) --build-arg GID=$(id -g)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We build and install HLS during image build time. There are prebuilt binaries available, but my past experience tells me HLS works properly only if built by the exact same GHC used to build your projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start the container after the dev image is built. Don’t forget to mount your working directory.&lt;/p&gt;

&lt;p&gt;The dev container is supposed to be a long-running container, and the simplest way to keep it running is using a &lt;code&gt;screen&lt;/code&gt; or &lt;code&gt;tmux&lt;/code&gt; session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional: setting up Docker context
&lt;/h2&gt;

&lt;p&gt;Skip this part if you’re using Docker on your local machine. If you’re using a remote machine, the previous steps are done within an SSH session, then you also need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the Docker CLI locally. It doesn’t need to be a full Docker installation, we don’t need to connect to the Docker daemon.&lt;/li&gt;
&lt;li&gt;Set up the Docker context using the instructions &lt;a href="https://code.visualstudio.com/docs/containers/ssh"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, the local &lt;code&gt;docker&lt;/code&gt; commands will transparently talk to the remote Docker daemon using SSH.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the dev container
&lt;/h2&gt;

&lt;p&gt;The rest is simple: enable the VSCode remote extension pack, click on the green button on VSCode window’s bottom left corner to open the remote menu, then select “Attach to Running Container”, voila. Set up extensions you use, open your working directory, and happy hacking GHC.&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>ghc</category>
    </item>
  </channel>
</rss>
