<?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: Michael Lohr</title>
    <description>The latest articles on DEV Community by Michael Lohr (@michidk).</description>
    <link>https://dev.to/michidk</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%2F363643%2Fc00a60a4-7bc9-4a08-905c-09a18f489b3b.jpeg</url>
      <title>DEV Community: Michael Lohr</title>
      <link>https://dev.to/michidk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michidk"/>
    <language>en</language>
    <item>
      <title>After a day of programming in Zig</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Mon, 01 Jan 2024 17:05:51 +0000</pubDate>
      <link>https://dev.to/michidk/after-a-day-of-programming-in-zig-463f</link>
      <guid>https://dev.to/michidk/after-a-day-of-programming-in-zig-463f</guid>
      <description>&lt;p&gt;I am a big fan of Rust since it provides great tooling and allows me to write code with a lot of confidence in that it will work reliably. But sometimes I hate it, too. It takes more time to write code in Rust and some things are pretty difficult to properly implement (looking at you async).&lt;/p&gt;

&lt;p&gt;In the last year, I heard a couple of times about a new low-level programming language called Zig. And now I finally found the time to try it out. In this article, I want to talk about the things I liked and disliked about Zig from the perspective of a Rust developer (and the high standards they are used to 🦀👑).&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is Zig?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ziglang.org/" rel="noopener noreferrer"&gt;Zig&lt;/a&gt; describes itself as "... a general-purpose programming language and toolchain for maintaining &lt;strong&gt;robust&lt;/strong&gt;, &lt;strong&gt;optimal&lt;/strong&gt; and &lt;strong&gt;reusable&lt;/strong&gt; software.". Sounds very generic, eh?&lt;/p&gt;

&lt;p&gt;The "unique selling points" are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No hidden control flow; you control everything&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No hidden memory allocation; they are all explicit and allow to use different allocation strategies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No preprocessor, no macros; directly write compile time code in Zig instead&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Great interoperability with C/C++; supports cross-compilation; can be used as a drop-in replacement for C.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zig is a bit similar to Rust since both focus on performance and safety. They don't use garbage collection, use LLVM as the compiler backend, and provide code testing capabilities. Both use a modern syntax and offer programming features like error handling and options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"std"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cd"&gt;/// Removes the specified prefix from the given string if it starts with it.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;removePrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="py"&gt;.mem&lt;/span&gt;&lt;span class="nf"&gt;.startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="py"&gt;.len&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though it simplifies a lot, I like to say that Zig is to C what Rust to C++ is. Others say, that Zig is the modern successor of C. As a rule of thumb, it probably makes sense to use Zig in projects where you would have used C before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it good?
&lt;/h2&gt;

&lt;p&gt;I usually learn new programming languages by simply writing some simple programs from start to finish. In this case, my goal was to write a &lt;a href="https://en.wikipedia.org/wiki/Telnet" rel="noopener noreferrer"&gt;telnet&lt;/a&gt; client (an old network protocol for remote access to terminals). This was quite a journey since telnet is a lot more complex than it seems. But this deserves an article on its own.&lt;/p&gt;

&lt;p&gt;It actually took me more than one day to implement this project, but after a full day of working on it, I had the feeling that I understood the basics of Zig. You can find the source code here: &lt;a href="https://github.com/michidk/telnet-zig/" rel="noopener noreferrer"&gt;https://github.com/michidk/telnet-zig/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What I dislike about Zig
&lt;/h3&gt;

&lt;p&gt;The barrier of liking Zig is not too high, since I really dislike programming in C. So let's first discuss the things I disliked:&lt;/p&gt;

&lt;p&gt;The Zig community and ecosystem are rather small, and not many libraries are available. Those that are available are also not very fleshed out yet. This is very different for Rust, where you can find at least one very popular and well-implemented crate for each problem which you might want to outsource to a library.&lt;/p&gt;

&lt;p&gt;Zig comes with a standard library that is similarly minimalistic like the Rust standard library. It is yet rather small but carefully designed. The documentation is not very good and many methods are undocumented.&lt;/p&gt;

&lt;p&gt;Undocumented code from &lt;code&gt;std.io.Writer&lt;/code&gt; (&lt;a href="https://ziglang.org/documentation/master/std/#A;std:io.Writer" rel="noopener noreferrer"&gt;https://ziglang.org/documentation/master/std/#A;std:io.Writer&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gn6nmxj2g2cb81ybx3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gn6nmxj2g2cb81ybx3h.png" alt="Undocumented code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is no proper pattern matching in Zig. However, &lt;code&gt;switch&lt;/code&gt; statements are quite powerful and when nesting them it is possible to achieve something similar, like one can do with Rust's &lt;code&gt;match&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;In Rust, traits (or interfaces in other languages) allow for polymorphism - the ability to write code that can operate on objects of different types. This is a powerful feature for designing flexible and reusable software components. Zig, however, lacks this feature. It relies on other mechanisms like function pointers or comptime polymorphism, which can be less intuitive and more cumbersome for scenarios typically handled by interfaces or traits.&lt;/p&gt;

&lt;p&gt;But to be honest, I wouldn't have expected otherwise, since Zig is rather young and only recently gained in popularity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65l028yx4188q67rohgz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65l028yx4188q67rohgz.png" alt="GitHub Star history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I love Zig
&lt;/h3&gt;

&lt;p&gt;Zig is great! It makes writing code fun in a similar way like Rust does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tooling, Build System &amp;amp; Tests
&lt;/h3&gt;

&lt;p&gt;Even though the language is rather young, the tooling is great! However is not as advanced as the Rust tooling with &lt;code&gt;cargo&lt;/code&gt; and &lt;code&gt;clippy&lt;/code&gt; (yet). Only the most recent version &lt;code&gt;v0.11&lt;/code&gt; delivered us an official package manager, called &lt;a href="https://zig.news/edyu/zig-package-manager-wtf-is-zon-558e" rel="noopener noreferrer"&gt;Zon&lt;/a&gt;. It can be used together with the &lt;code&gt;build.zig&lt;/code&gt; file (which is similar to a &lt;code&gt;build.rs&lt;/code&gt; in Rust) to load libraries from GitHub into our project without much hassle (I don't even want to know how much valuable lifetime &lt;code&gt;cmake&lt;/code&gt; and &lt;code&gt;make&lt;/code&gt; have cost me in the past).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zig build-exe hello.zig
&lt;span class="nv"&gt;$ &lt;/span&gt;./hello
Hello, world!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly to Rust, Zig comes with robust testing capabilities built into the language. Tests in Zig are written in special functions, allowing them to reside alongside the code they are validating. Unique to Zig is the ability to leverage its compile-time evaluation features in tests. Additionally, Zig's support for cross-compilation in testing is particularly noteworthy, enabling developers to effortlessly test their code across various target architectures. &lt;a href="https://mtlynch.io/notes/zig-unit-test-c/" rel="noopener noreferrer"&gt;Some people&lt;/a&gt; even use Zig to test their C code!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"std"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;parseInt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="py"&gt;.fmt.parseInt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Unit testing&lt;/span&gt;
&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s"&gt;"parse integers"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ally&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="py"&gt;.testing.allocator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="nf"&gt;.ArrayList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ally&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="nf"&gt;.deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The Language
&lt;/h4&gt;

&lt;p&gt;The language itself is well-designed and the syntax is quite similar to Rust. Both have a type system that emphasizes strong, static typing, though the way they handle types and type inference differs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Error Handling and Optionals
&lt;/h4&gt;

&lt;p&gt;Zig and Rust both promote explicit error handling, however their mechanisms are different. Rust uses &lt;code&gt;Result&lt;/code&gt; enums, while Zig uses a (global) error set type (though similar to an enum) and error propagation. Similarly, Rust uses the &lt;code&gt;Option&lt;/code&gt; enum for optional types, while Zig uses a type modifier (&lt;code&gt;?T&lt;/code&gt;). Both offer modern, syntactic sugar to handle those (&lt;code&gt;call()?&lt;/code&gt; and &lt;code&gt;if let Some(value) = optional {}&lt;/code&gt; in Rust, &lt;code&gt;try call()&lt;/code&gt; and &lt;code&gt;if (optional) |value| {}&lt;/code&gt; in Zig). Since Rust uses the standard library to implement error handling and options, users have the possibility to extend those systems which is quite powerful. However, I like the approach Zig takes in providing those things as language features. While their approach fits well into the C universe, I dislike that there is no pragmatic way to add more context to an error (but well, no allocations). Libraries like [clap](&lt;a href="https://github.com/Hejsil/zig-clap" rel="noopener noreferrer"&gt;https://github.com/Hejsil/zig-clap&lt;/a&gt;) solve this by implementing a diagnostics mechanism.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Hello World in Zig&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"std"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nd"&gt;anyerror!&lt;/span&gt;&lt;span class="n"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="py"&gt;.io&lt;/span&gt;&lt;span class="nf"&gt;.getStdOut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.writer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="nf"&gt;.print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, {s}!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; .&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  C Interop
&lt;/h4&gt;

&lt;p&gt;The C interoperability in Zig is world-class. You don't need to write bindings, with Zig you can just use the &lt;code&gt;@cImport&lt;/code&gt; and &lt;code&gt;@cInclude&lt;/code&gt; built-in functions (which parses C header files) to directly use C code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Comptime
&lt;/h4&gt;

&lt;p&gt;Zig allows us to write Zig code (no special macro syntax like in Rust), which is evaluated during compile time using the &lt;code&gt;comptime&lt;/code&gt; keyword. This can help to optimize the code and allows for reflection on types. However, dynamic memory allocations are not allowed during compile time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Types
&lt;/h4&gt;

&lt;p&gt;Like in Rust, Zig types are zero-cost abstractions. There are Primitive Types, Arrays, Pointers, Structs (similar to C structs, but can include methods), Enums and Unions. Custom types are implemented through structs and generics are implemented by generating parameterized structs during compile time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// std.io.Writer is a compile-time function which returns a (generic) struct&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;Writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;comptime&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;comptime&lt;/span&gt; &lt;span class="n"&gt;WriteError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;comptime&lt;/span&gt; &lt;span class="n"&gt;writeFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;WriteError!&lt;/span&gt;&lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;This&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WriteError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;Error!&lt;/span&gt;&lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;writeFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Memory Allocation
&lt;/h4&gt;

&lt;p&gt;Unlike Rust, which employs an automatic borrow-checker to manage memory, Zig opts for manual memory management. This design decision aligns with Zig's philosophy of giving the programmer full control, reducing hidden behaviors and overhead.&lt;/p&gt;

&lt;p&gt;At the core of Zig's memory management strategy is the &lt;code&gt;Allocator&lt;/code&gt; interface. This interface allows developers to dictate precisely how memory is allocated and deallocated. Developers can choose from &lt;a href="https://ziglang.org/documentation/0.5.0/#Choosing-an-Allocator" rel="noopener noreferrer"&gt;several allocators&lt;/a&gt; or implement custom ones tailored to specific needs or optimization goals.&lt;/p&gt;

&lt;p&gt;This is great but can be a bit annoying in practice. The allocator is typically created at the beginning of the application code and assigned to a variable. Methods which want to allocate memory, require the allocator as a parameter in their function signature. This makes allocations very visible but also can get a bit annoying because it has to be passed around through multiple functions throughout the whole application (at least to the parts where you allocated memory).&lt;/p&gt;

&lt;h4&gt;
  
  
  Cross Compilation
&lt;/h4&gt;

&lt;p&gt;Zig, like Rust, has native support for cross-compilation. Its integrated toolchain simplifies compiling for different architectures or operating systems. Setting the target architecture in Zig is as straightforward as passing an argument in the build command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build for Windows on Linux&lt;/span&gt;
zig build &lt;span class="nt"&gt;-Dtarget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x86_64-windows-gnu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In contrast, Rust requires the installation of the target platform's toolchain through &lt;code&gt;rustup&lt;/code&gt; and often necessitates manual linker configuration for the target platform.&lt;/p&gt;

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

&lt;p&gt;I find Zig to be a well-designed, fun, and powerful language. It can be challenging to use because of the small ecosystem and the lack of documentation, which most likely will improve soon with increasing popularity. Zig provides modern syntax, a great type system, complete control over memory allocations and state-of-the-art language features.&lt;/p&gt;

&lt;p&gt;Overall, I enjoyed programming in Zig and I think it has a lot of potential to become a popular choice for low-level development. Personally, I think Zig could be a real game changer for embedded systems (in a few years) and I am quite excited to see what the future holds for Zig.&lt;/p&gt;

&lt;p&gt;I encourage you to give Zig a try⚡.&lt;/p&gt;

</description>
      <category>zig</category>
      <category>rust</category>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>Reverse engineering Microsoft's dev container CLI</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Wed, 15 Nov 2023 19:35:30 +0000</pubDate>
      <link>https://dev.to/michidk/launching-dev-containers-from-code-is-impossible-3efa</link>
      <guid>https://dev.to/michidk/launching-dev-containers-from-code-is-impossible-3efa</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Dev containers allow you to open up projects and have them running in a fully pre-configured environment with one click!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;... is how I introduced the concept of &lt;a href="https://containers.dev/"&gt;dev containers&lt;/a&gt; in &lt;a href="https://blog.lohr.dev/dev-containers"&gt;my last article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also mentioned that I have been working on a &lt;a href="https://github.com/michidk/vscli"&gt;small utility named vscli,&lt;/a&gt; which enables you to launch Visual Studio Code (vscode) from the command line (CLI) or a terminal user interface (TUI) like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jMbtaj9W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjzq6f2cie7h0a4my1xi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jMbtaj9W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjzq6f2cie7h0a4my1xi.png" alt="Image description" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now this does not look very complicated, right? The cool thing is (and actually the main problem I want to solve with this tool), that it can also open devcontainers. So if it detects that the folder you are trying to open, with e.g. &lt;code&gt;vscli dev/myproject&lt;/code&gt;, contains a dev container configuration file, it will open it in a vscode dev container instead.&lt;/p&gt;

&lt;p&gt;Now, you might think it is as easy as calling a command like &lt;code&gt;code dev/myproject --devcontainer&lt;/code&gt; behind the scenes. You couldn't be more wrong!&lt;/p&gt;

&lt;p&gt;Before explaining how this actually works, I want to let you know that it will get a lot weirder when we try to add support for a new dev container feature later.&lt;/p&gt;

&lt;h2&gt;
  
  
  How everybody is doing it
&lt;/h2&gt;

&lt;p&gt;This is not a problem that hasn't been solved before. Lots of people want to start their dev containers right from the CLI, without navigating menus in vscode. So if you look through various bash scripts and some issues on GitHub, you will find that most people solve this by executing the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code &lt;span class="nt"&gt;--folder-uri&lt;/span&gt; vscode-remote://dev-container+&amp;lt;some_weird_hex_string&amp;gt;&amp;lt;a_path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which then could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code &lt;span class="nt"&gt;--folder-uri&lt;/span&gt; &lt;span class="s2"&gt;"vscode-remote://dev-container+5c5c77736c2e6c6f63616c686f73745c417263685c686f6d655c6d696368695c6465765c7673636c69/workspaces/vscli
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last part (&lt;code&gt;/workspaces/vscli&lt;/code&gt; in this case) is the path we want vscode to open inside the container. It is &lt;code&gt;/workspaces/project_folder_name&lt;/code&gt; by default, but it can be overwritten inside the dev container configuration file.&lt;/p&gt;

&lt;p&gt;The hex value is the path on the host, encoded in hex. Decoded could look like this (when the dev container was launched from &lt;a href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux"&gt;WSL&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="se"&gt;\\&lt;/span&gt;wsl.localhost&lt;span class="se"&gt;\A&lt;/span&gt;rch&lt;span class="se"&gt;\h&lt;/span&gt;ome&lt;span class="se"&gt;\m&lt;/span&gt;ichi&lt;span class="se"&gt;\d&lt;/span&gt;ev&lt;span class="se"&gt;\v&lt;/span&gt;scli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: We used &lt;a href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux"&gt;WSL&lt;/a&gt; here, so most paths will be WSL paths. However, it works quite similarly on Windows.&lt;/p&gt;

&lt;h3&gt;
  
  
  How did we figure that out?
&lt;/h3&gt;

&lt;p&gt;Well, I got the hint from &lt;a href="https://github.com/devcontainers/cli/issues/30"&gt;this GitHub issue&lt;/a&gt;. Also, it seems like at one time a &lt;code&gt;devcontainer open&lt;/code&gt; command existed in &lt;a href="https://www.npmjs.com/package/@vscode/dev-container-cli"&gt;the old dev container CLI&lt;/a&gt; (it does not exist anymore since the CLI wants to be editor-agnostic).&lt;/p&gt;

&lt;p&gt;There is also another version of the dev container CLI, which is included in the proprietary vscode dev container extension. This version actually includes the &lt;code&gt;devcontainer open&lt;/code&gt; utility, but we only have access to the minified JavaScript code.&lt;/p&gt;

&lt;p&gt;So if this problem is already solved, why implement our own CLI tool? Well, there are multiple reasons: closed-source, sending telemetry by default, and cannot be installed with a proper packet manager.&lt;/p&gt;

&lt;h2&gt;
  
  
  But we want multiple containers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/michidk/vscli/issues/24"&gt;It was brought to my attention&lt;/a&gt;, that the dev containers spec/vscode released &lt;a href="https://github.com/microsoft/vscode-docs/blob/main/remote-release-notes/v1_75.md#folders-with-multiple-devcontainerjson-files"&gt;a new feature&lt;/a&gt;, which would allow having multiple dev container definitions inside one project (by creating multiple configuration files in different places). I needed to try it out immediately, and actually found several use cases in my projects - so vscli needs to support it as well!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jMP1OVqW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c4i3rx3tfqrxapixkopl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jMP1OVqW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c4i3rx3tfqrxapixkopl.png" alt="Image description" width="599" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But how would I tell vscode which container to open? Using the strategy mentioned earlier, there is no way to point to some specific config. I did some research, and I haven't found a single codebase or shell script containing code to open dev containers in any other way than the command above. So nobody either managed or cared enough to figure this out. Is it even possible? Well, it has to, since vscode does it too!&lt;/p&gt;

&lt;h3&gt;
  
  
  Down the rabbit hole...
&lt;/h3&gt;

&lt;p&gt;So I sat down together with &lt;a href="https://github.com/Shemnei/"&gt;my buddy&lt;/a&gt; and did a late-night investigation. Here is how it went (spoiler: we actually figured it out in the end).&lt;/p&gt;

&lt;p&gt;First, we had a look at the closed-source version of dev container CLI again, maybe it supports this? Turns out it does!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GyQp8L1U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10wyoc0v2l6utpuz6b0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GyQp8L1U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10wyoc0v2l6utpuz6b0e.png" alt="Image description" width="800" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we downloaded the source code of the dev container extension from the official marketplace: &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"&gt;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers&lt;/a&gt;. This gives us the &lt;code&gt;ms-vscode-remote.remote-containers-0.320.0.vsix&lt;/code&gt; file. Since &lt;code&gt;.vsix&lt;/code&gt; is basically just a &lt;code&gt;.zip&lt;/code&gt; file, after extracting it we could explore the extension.&lt;/p&gt;

&lt;p&gt;Luckily they not only included the compiled binary but also the Node JavaScript "source code": &lt;code&gt;extension/dev-containers-user-cli/dist/node/devContainersUserCLI.js&lt;/code&gt;. However, it's not real source code, since the source application is probably written in TypeScript, and what we get to see is the minified, transpiled JavaScript:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PEafzGG_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i1abe943nx4ioqmhqarg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PEafzGG_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i1abe943nx4ioqmhqarg.png" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code is optimized for minimal size, so most of the functions and variable names are renamed to one-letter names and everything is put into one line without spaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making sense of the code
&lt;/h3&gt;

&lt;p&gt;Since this code is unreadable, it is very hard to get the control flow. We started by searching for strings we know, like &lt;code&gt;vscode-remote://dev-container&lt;/code&gt;. And we had a match! The extracted and formatted function which uses this string looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Z$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;moe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;hostPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;localDocker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`vscode-remote://dev-container+&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;)}${&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that whatever is put behind the &lt;code&gt;+&lt;/code&gt; (variable &lt;code&gt;s&lt;/code&gt;) is converted into hex, as we already know. &lt;code&gt;n&lt;/code&gt; is the workspace path and the final part of the URI. &lt;code&gt;t&lt;/code&gt; is an optional parameter I am not quite sure of. But if we look into how &lt;code&gt;s&lt;/code&gt; (the hex-encoded part) is defined, we can see that it is either &lt;code&gt;e&lt;/code&gt; or some JSON string depending on how &lt;code&gt;r || o&lt;/code&gt; evaluated.&lt;/p&gt;

&lt;p&gt;So this seems to be the solution to the problem! It is possible to pass in more data than just the project path! We don't really care for &lt;code&gt;r&lt;/code&gt; and &lt;code&gt;o&lt;/code&gt; at this point, but if we look into how the JSON string is constructed, we can even read what they are: &lt;code&gt;r&lt;/code&gt; seems to be some additional &lt;code&gt;settings&lt;/code&gt; and &lt;code&gt;o&lt;/code&gt; the &lt;code&gt;configFile&lt;/code&gt;? &lt;code&gt;e&lt;/code&gt; is the &lt;code&gt;hostPath&lt;/code&gt;, which is also used when we don't use the JSON string. So this is the path to the project we also used in the old approach. &lt;code&gt;i&lt;/code&gt; which is put into the &lt;code&gt;localDocker&lt;/code&gt; field is probably some boolean that describes whether to use a local or remote Docker host.&lt;/p&gt;

&lt;h3&gt;
  
  
  Did we solve it?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;configFile&lt;/code&gt; parameter is probably the file path of the dev container config file we are trying to load. So let's build up the following JSON and try to open vscode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hostPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;wsl.localhost&lt;/span&gt;&lt;span class="se"&gt;\A&lt;/span&gt;&lt;span class="s2"&gt;rch&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;ome&lt;/span&gt;&lt;span class="se"&gt;\m&lt;/span&gt;&lt;span class="s2"&gt;ichi&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;ev&lt;/span&gt;&lt;span class="se"&gt;\v&lt;/span&gt;&lt;span class="s2"&gt;scli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"configFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;wsl.localhost&lt;/span&gt;&lt;span class="se"&gt;\A&lt;/span&gt;&lt;span class="s2"&gt;rch&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;ome&lt;/span&gt;&lt;span class="se"&gt;\m&lt;/span&gt;&lt;span class="s2"&gt;ichi&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;ev&lt;/span&gt;&lt;span class="se"&gt;\v&lt;/span&gt;&lt;span class="s2"&gt;scli&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;devcontainer&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;estContainer.json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which opens vscode, but it will complain with the following error: &lt;code&gt;\[UriError\]: Scheme contains illegal character&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We tested a few options to debug this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Only using &lt;code&gt;hostPath&lt;/code&gt; without &lt;code&gt;configFile&lt;/code&gt;: works (but we cannot choose a dev container config)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Directly putting the dev container config file contents into &lt;code&gt;configFile&lt;/code&gt;: &lt;code&gt;UriError&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using Windows style file paths: &lt;code&gt;UriError&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So &lt;code&gt;configFile&lt;/code&gt; expects some special URI format? It's time to look into the code a bit more: &lt;code&gt;Z$&lt;/code&gt; is called only once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Z&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspaceFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;qn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Ze&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most parameters are &lt;code&gt;void 0&lt;/code&gt;, which is the shorter equivalent of &lt;code&gt;undefined&lt;/code&gt;. This shows that &lt;code&gt;r&lt;/code&gt;, &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;t&lt;/code&gt; of &lt;code&gt;Z$&lt;/code&gt; are actually not used (probably for legacy reasons), so we always use the JSON format nowadays. We also learn how the input to the &lt;code&gt;hostPath&lt;/code&gt; is constructed: &lt;code&gt;qn.file(Ze.resolve(process.cwd(), e))&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At first look, it looks like it combines the path of the current folder (&lt;code&gt;process.cwd()&lt;/code&gt;) and the relative path to the dev container config file and then passes it into a function that builds the file representation that the URI expects. After some investigation and finally ended up with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;LP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ae&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;os&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
 &lt;span class="nx"&gt;Ze&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ae&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We concluded that &lt;code&gt;Ze.resolve()&lt;/code&gt; is part of the &lt;a href="https://nodejs.org/api/path.html#pathresolvepaths"&gt;node.js path&lt;/a&gt; library and indeed combines path segments.&lt;/p&gt;

&lt;h3&gt;
  
  
  The last mystery: &lt;code&gt;.file()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This one was difficult. We really could not make sense of this method, since it was part of a bigger library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;qn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Utils&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;awe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// and then somewhere else in the code&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}));&lt;/span&gt;
  &lt;span class="p"&gt;})(),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Luckily, somewhere in this library, we found an error message: &lt;code&gt;The "pathObject" argument must be of type Object. Received type ...&lt;/code&gt;. I was never as excited before to find an error message. But this is something we can Google and maybe find out which library it is!&lt;/p&gt;

&lt;p&gt;And it worked! We found a &lt;a href="https://github.com/microsoft/vscode/issues/93220"&gt;GitHub issue&lt;/a&gt; in the vscode GitHub repo, which referenced the &lt;a href="https://github.com/microsoft/vscode/blob/main/src/vs/base/common/uri.ts"&gt;exact file it is implemented in&lt;/a&gt;. Turns out this is the internal URI library of vscode!&lt;/p&gt;

&lt;p&gt;The code is still quite complex, but there was an easier way anyways. I just opened up the developer tools in vscode and called the &lt;code&gt;.file()&lt;/code&gt; function directly, passing in the path to my dev container config:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wYolYBQe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xodsn9b2ucih8mypoe5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wYolYBQe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xodsn9b2ucih8mypoe5u.png" alt="Image description" width="792" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the &lt;code&gt;.file()&lt;/code&gt; method returns an object that is directly serialized into JSON. The &lt;code&gt;UriError&lt;/code&gt; message from before probably hinted at the &lt;code&gt;"scheme":"file",&lt;/code&gt; property missing. This does not look like a proper URI interface and is probably an accident, where the developers forgot to properly serialize the path - but hey, we figured it out!&lt;/p&gt;

&lt;p&gt;So now we can use the following JSON to open our dev containers with multiple config files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hostPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;wsl.localhost&lt;/span&gt;&lt;span class="se"&gt;\A&lt;/span&gt;&lt;span class="s2"&gt;rch&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;&lt;span class="s2"&gt;ome&lt;/span&gt;&lt;span class="se"&gt;\m&lt;/span&gt;&lt;span class="s2"&gt;ichi&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="s2"&gt;ev&lt;/span&gt;&lt;span class="se"&gt;\v&lt;/span&gt;&lt;span class="s2"&gt;scli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"$mid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;optional&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/Arch/home/michi/temp/multi/.devcontainer.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"authority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wsl.localhost"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is still some work to do with getting the paths in the proper format and to make this work on Windows, but this should get you started if you want to implement this yourself. Check out the implementation of this in vscli here: &lt;a href="https://github.com/michidk/vscli/blob/main/src/uri.rs"&gt;https://github.com/michidk/vscli/blob/main/src/uri.rs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if you try to open a project with more than one dev container using vscli, the following dialog will appear:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YWUZzYo6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nr5d62vjy0ez5nijftii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YWUZzYo6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nr5d62vjy0ez5nijftii.png" alt="Image description" width="674" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to use vscli yourself? Check it out on GitHub: &lt;a href="https://github.com/michidk/vscli"&gt;https://github.com/michidk/vscli&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devcontainer</category>
      <category>vscode</category>
      <category>javascript</category>
      <category>cli</category>
    </item>
    <item>
      <title>Dev Containers: Open, Develop, Repeat...</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Mon, 30 Oct 2023 22:16:58 +0000</pubDate>
      <link>https://dev.to/michidk/dev-containers-open-develop-repeat-h0l</link>
      <guid>https://dev.to/michidk/dev-containers-open-develop-repeat-h0l</guid>
      <description>&lt;p&gt;Dev containers allow you to open up projects and have them running in a fully pre-configured environment with one click!&lt;/p&gt;

&lt;p&gt;It is especially useful if you want to keep your system clean of different SDKs with different (&lt;em&gt;conflicting&lt;/em&gt;) Versions (looking at you Python 👀) or work on many different projects, which require different setups. It is also helpful to provide a dev container for other people to start working on your projects because they get up and running within seconds!&lt;/p&gt;

&lt;p&gt;I have used them for more than two years and it made my developer workflow so much easier. I nowadays use them in every project - work and private!&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;How it works? &lt;a href="https://containers.dev/" rel="noopener noreferrer"&gt;Dev containers&lt;/a&gt; is a specification based on &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;. This specification describes a &lt;a href="https://containers.dev/implementors/json_reference/" rel="noopener noreferrer"&gt;metadata file&lt;/a&gt; (&lt;code&gt;devcontainer.json&lt;/code&gt;), which defines how the project (Docker container, IDE settings, plugins, etc) is set up.&lt;/p&gt;

&lt;p&gt;It can look as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rust"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcr.microsoft.com/devcontainers/rust:1-bullseye"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or get more complex like, this configuration which is based on a local Dockerfile, installs "dev container features" (will be explained later), configures some extensions, and my terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Rust IDE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dockerfile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dockerfile"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ghcr.io/guiyomh/features/just:0"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customizations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"vscode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"rust-lang.rust-analyzer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"tamasfe.even-better-toml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"serayuzgur.crates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"kokakiwi.vscode-just"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"terminal.integrated.defaultProfile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zsh"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from the &lt;code&gt;customizations&lt;/code&gt; section, dev containers are IDE agnostic. For example, there is a project implementing &lt;a href="https://github.com/esensar/nvim-dev-container" rel="noopener noreferrer"&gt;dev container support into nvim&lt;/a&gt;. But since the &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; (vscode) team at Microsoft invented dev containers, it is currently the IDE with the best dev container experience.&lt;/p&gt;

&lt;p&gt;A dev container can be bootstraped by using the vscode UI or creating the configuration file by hand. There are a bunch of pre-made dev container configurations maintained by the vscode team and community ready to be used:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flio9f85ngzc7o9433twj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flio9f85ngzc7o9433twj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This video is a good resource on how to learn the basics of dev containers and how to use them in vscode:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/b1RavPr_878"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Dev Container Features
&lt;/h2&gt;

&lt;p&gt;dev containers not only allow you to define which extensions should be installed and which configuration settings shall be set, but they also have something they call &lt;a href="https://github.com/devcontainers/features" rel="noopener noreferrer"&gt;"dev container features"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;They are reusable modules that contain installation scripts and dev container configurations. This means allows you to configure your development environment even quicker (and install them using a vscode UI).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5du1nfvdeahq9ofzu0a3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5du1nfvdeahq9ofzu0a3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my example above, I installed the developer tool "&lt;a href="https://github.com/casey/just" rel="noopener noreferrer"&gt;Just&lt;/a&gt;" as a dev container feature. I could also install it by adding the install script to my Dockerfile. However, I would have to build my own Dockerfile and would have to maintain this piece of code myself. This dev container Feature works on different architectures and base images, which makes them convenient to use.&lt;/p&gt;

&lt;p&gt;A list of available dev container features, can be found &lt;a href="https://containers.dev/features" rel="noopener noreferrer"&gt;here&lt;/a&gt;. However, you can also develop your own features and publish them, &lt;a href="https://github.com/michidk/devcontainers-features/" rel="noopener noreferrer"&gt;like I did&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Opening dev containers From the CLI
&lt;/h1&gt;

&lt;p&gt;So you can use dev containers from the vscode user interface rather intuitively. All configurations can also be edited directly and there is even a &lt;a href="https://github.com/devcontainers/cli" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;. However, this CLI is made editor agnostic, so there is no vscode integration.&lt;/p&gt;

&lt;p&gt;What I have been always missing was just a simple command to open up a dev container in my current directory, inside vscode. Turns out it is not that easy!&lt;/p&gt;

&lt;p&gt;Now, there is a proprietary dev container CLI version included in vscode (which can be only installed by adding &lt;code&gt;C:\Users\&amp;lt;USER&amp;gt;\AppData\Roaming\Code\User\globalStorage\ms-vscode-remote.remote-containers\cli-bin&lt;/code&gt; to the &lt;code&gt;PATH&lt;/code&gt;), that offers a &lt;code&gt;devcontainer open&lt;/code&gt; command. I am not sure if there is a version for Linux as well. But what I am sure of, is that it sends some kind of telemetry data to Microsoft by default.&lt;/p&gt;

&lt;p&gt;I was not happy with this solution, so I built &lt;a href="https://github.com/michidk/vscli" rel="noopener noreferrer"&gt;vscli&lt;/a&gt;, a vscode CLI tool to launch projects and support dev containers. It works on most platforms and is written in Rust. It includes a simple terminal UI, which allows you to quick-launch your recently opened projects:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxas5d5ucti0jg39le06n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxas5d5ucti0jg39le06n.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check it out here: &lt;a href="https://github.com/michidk/vscli" rel="noopener noreferrer"&gt;https://github.com/michidk/vscli&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Codespaces
&lt;/h2&gt;

&lt;p&gt;dev containers also power &lt;a href="https://github.com/features/codespaces" rel="noopener noreferrer"&gt;GitHub Codespaces&lt;/a&gt;, which allows you to have the same dev container experience in the Browser running in the Cloud!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7iskc4uaxmzh1d51dqx0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7iskc4uaxmzh1d51dqx0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a bit like &lt;code&gt;github.dev&lt;/code&gt; (you can replace the &lt;code&gt;.com&lt;/code&gt; with a &lt;code&gt;.dev&lt;/code&gt; or just press &lt;code&gt;.&lt;/code&gt; on any GitHub repo and you will get the project editable in a vscode web version), but with all the extensions and dev container running on a machine in the Cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;I recommend you to check out dev containers, it made my life so much easier. At this stage, they are also pretty mature and supported by a large community.&lt;/p&gt;

&lt;p&gt;I even know some VIM people, using vscode from time to time just because of dev containers 😉&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>devcontainer</category>
      <category>extensions</category>
      <category>tools</category>
    </item>
    <item>
      <title>Making IBANs more memorable</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Sun, 23 Jul 2023 11:01:29 +0000</pubDate>
      <link>https://dev.to/michidk/making-ibans-more-memorable-3agp</link>
      <guid>https://dev.to/michidk/making-ibans-more-memorable-3agp</guid>
      <description>&lt;p&gt;Or: "How I fixed IBANs with Bitcoin". This article is supposed to walk you through the journey I had while exploring an idea I had in mind for making IBANs more memorable.&lt;/p&gt;

&lt;p&gt;The IBAN (International Bank Account Number) is a standardized international numbering system for bank accounts. It's used across countries to send money securely from one bank account to another.&lt;/p&gt;

&lt;p&gt;In Germany, an IBAN might look like this: &lt;code&gt;DE67834783927384738238\&lt;/code&gt;. Now if you lend someone money for lunch (because they didn't have cash on them) and want it back, you would send them your IBAN. Now you have three options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Log in (and authenticate) to your banking app and copy &amp;amp; paste the IBAN&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy it out from your notes app, where you wrote it down before&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Take out your banking card and copy the IBAN number that is printed on it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You both have PayPal, and you just share your email&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You are a maniac and know your IBAN by heart&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All those options are a bit annoying and dependent on how often you find yourself in such a scenario, you might get pissed off about how unmemorable IBANs are. I am pissed off about how unmemorable IBANs are (in case you wondered).&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;You might have heard of BIP-0039. No, you probably didn't, but you might have seen something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;canyon situate farm wedding cluster budget truck bag goose
obtain surround soda cable galaxy spoil utility tip remember
scan danger cat lawsuit staff riot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a Bitcoin wallet seed encoded in the so-called &lt;em&gt;mnemonic code&lt;/em&gt;, which was proposed in &lt;a href="https://en.bitcoin.it/wiki/BIP_0039"&gt;BIP-0039&lt;/a&gt;. It is easier to remember, verbally communicate and write down than binary or hexadecimal data. This would be really handy to have for IBANs as well!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want my IBAN to be a 'simple cluster truck wedding bag goose soda galaxy'&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Bitcoin implementation comes with a few Bitcoin-specific add-ons, which we don't need. So we could just follow the implementation by &lt;a href="https://web.archive.org/web/20100105040244/http://tothink.com/mnemonic/index.html"&gt;Oren Tirosh&lt;/a&gt;, which everybody seems to reference when talking about mnemonic code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Theory
&lt;/h2&gt;

&lt;p&gt;The Mnemonic encoding by Oren works by taking a segment of bytes and calculating an index that maps to a word of a wordlist. This is not just one random wordlist extracted from a dictionary. The words are carefully selected by adhering to a set of criteria (which is heavily discussed on the internet), as seen &lt;a href="https://gist.github.com/fogleman/c4a1f69f34c7e8a00da8"&gt;here&lt;/a&gt;. So we just have to import some library and convert an IBAN into a bunch of bytes?&lt;/p&gt;

&lt;p&gt;Well, first, we have to discuss how we actually convert an IBAN to bytes. We want to use as few bytes as possible since each extra byte will result in additional words, which one must remember.&lt;/p&gt;

&lt;h2&gt;
  
  
  How IBANs work
&lt;/h2&gt;

&lt;p&gt;But in order to be able to properly encode IBANs into bytes, we first have to understand what they are and how they work.&lt;/p&gt;

&lt;p&gt;IBANs are defined in the ISO 13616-1 standard, which is actually quite readable (which I am not used to when reading standards). It defines that an IBAN consists of the following elements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Two-letter country code, aka "alpha-2 code", according to ISO 3166-1&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checksum consisting of two numbers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Up to 30 characters and numbers called the "BBAN"&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The BBAN has to have a fixed size per country code and also encode a bank identifier whose position and length are also fixed per country code.&lt;/p&gt;

&lt;p&gt;This means that each country has its own "sub-standard" for IBANs (or BBANs, to be specific).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--huAfj4pb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExbmY4ZmExbzFwbTczczVuNTRyMjk0bDJzcHAyZjc5bTd3endxNXEzZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ToMjGpIYtgvMP38WTFC/giphy.gif%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--huAfj4pb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExbmY4ZmExbzFwbTczczVuNTRyMjk0bDJzcHAyZjc5bTd3endxNXEzZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ToMjGpIYtgvMP38WTFC/giphy.gif%2520align%3D%2522center%2522" alt="" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The country code is often indexed by a numeric code that is bigger than 255, meaning it would not fit in one byte. But if you look at &lt;a href="https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes#Current_ISO_3166_country_codes"&gt;the actual country code list&lt;/a&gt;, there are just 249 entries. This means by simply numbering them from 0 to 248, we can store that index in just one byte.&lt;/p&gt;

&lt;p&gt;In theory, we could remove the checksum from the mnemonic code and &lt;a href="https://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits"&gt;recalculate it&lt;/a&gt; when parsing the code, which would remove one byte. Since mixing up a word is more unlikely than mixing up a number, this could be a valid consideration. However, I think mixing up the order of words is still pretty likely, so some kind of verification check is still necessary.&lt;/p&gt;

&lt;p&gt;Encoding the BAN is the difficult part: Each country has its own BBAN standard, as seen on &lt;a href="https://en.wikipedia.org/wiki/International_Bank_Account_Number#IBAN_formats_by_country"&gt;Wikipedia&lt;/a&gt;. It would be nice to have a way to support arbitrary IBAN numbers and convert them to bytes in the most space-efficient way. For this, one probably has to incorporate the country-specific BBAN specification to parse characters and numbers in the correct places (numbers need way fewer bytes than characters).&lt;/p&gt;

&lt;p&gt;For German IBANs, it's quite easy: After the checksum, there are 18 numeric characters left to parse. The first 8 are the bank identifier, and the following 10 are the account number. These 18 numbers characters can be interpreted as a 7-byte integer. No ASCII characters, which would increase the byte representation in size. Together with the country code, we arrive at 8 bytes in total.&lt;/p&gt;

&lt;p&gt;For comparison, here are some other countries with their IBAN format and required bytes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;IBAN Length&lt;/th&gt;
&lt;th&gt;Format (excluding country code and check digits)&lt;/th&gt;
&lt;th&gt;Bytes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Germany&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;8 numeric (BLZ), 10 numeric (Account No.)&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;France&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;5 numeric, 5 numeric, 11 numeric, 2 numeric&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;United Kingdom&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;4 alphanumeric (Sort Code), 6 numeric, 8 numeric&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spain&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;4 numeric, 4 numeric, 10 numeric, 2 numeric&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Italy&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;1 alphanumeric, 5 numeric, 5 numeric, 12 numeric&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Netherlands&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;4 alphanumeric, 10 numeric&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Belgium&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;3 numeric, 7 numeric, 2 numeric&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Most programming languages have libraries that already implement Oren's mnemonic encoder/decoder (e.g., &lt;a href="https://pypi.org/project/mnemonic/"&gt;Python&lt;/a&gt; or &lt;a href="https://pypi.org/project/mnemonic/"&gt;Rust&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So to implement the conversion from some IBAN string to mnemonic code, we would follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Split the country code of the IBAN, so that checksum and BBAN remains&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calculate the index of the country code and convert it to a byte&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Parse the checksum and BBAN as some big integer and convert them into bytes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Put the country code byte and other bytes together and feed them into the mnemonic encoding library&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To implement parsing the mnemonic code into an IBAN, we would just reverse the steps. If we discarded the checksum while encoding, we would have to &lt;a href="https://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits"&gt;recalculate it&lt;/a&gt; again. I also recommend verifying the IBAN using &lt;a href="https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN"&gt;its built-in checksum mechanism&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I would love to provide the source code of my implementation, but I would have to clean it up first. If you did a proper implementation of this, let me know!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Considerations
&lt;/h2&gt;

&lt;p&gt;This is a list of "add-ons" to this idea, which I might extend in the future.&lt;/p&gt;

&lt;p&gt;By discarding the IBAN checksum and implementing a custom &lt;a href="https://en.wikipedia.org/wiki/Cyclic_redundancy_check"&gt;crc-based checksum&lt;/a&gt;, one could reduce the storage footprint of the checksum.&lt;/p&gt;

&lt;p&gt;It would also be possible to separately encode the checksum in an extra word which is appended to the end. Then the "checksum word" would be optional, and the user could decide whether he wants to remember this additional word.&lt;/p&gt;

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

&lt;p&gt;This was a fun experiment that allowed me to dive deeper into topics that always interested me but never had a use case for. Maybe this idea is actually helpful - if you think so, let me know. I might write a simple web service that allows for an easy conversion. The problem with these things is that they are useless until not everybody (or some big banks) is adapting this. It would be really awesome if in the future, I could just enter some words into my banking app to send my money to someone. However, there are probably also some security considerations I haven't thought of.&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>mnemonic</category>
      <category>iban</category>
      <category>banking</category>
    </item>
    <item>
      <title>Changing the primary display on Windows by code is easy... right?</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Sat, 29 Oct 2022 10:27:27 +0000</pubDate>
      <link>https://dev.to/michidk/changing-the-primary-display-on-windows-by-code-is-easy-right-48df</link>
      <guid>https://dev.to/michidk/changing-the-primary-display-on-windows-by-code-is-easy-right-48df</guid>
      <description>&lt;p&gt;I have this issue where Windows is sometimes randomly changing my primary display after a system restart. So I wanted to create a simple command-line application that would allow me to change the display settings on system startup - should be easy, right 🤔?&lt;/p&gt;

&lt;p&gt;There are great tools like &lt;a href="https://www.nirsoft.net/utils/multi_monitor_tool.html"&gt;MultiMonitorTool&lt;/a&gt; that already provide this functionality, but they come with a lot of UI and utilities, that I don't need - I want a lightweight tool that I can include in my &lt;a href="https://blog.lohr.dev/automated-windows-setup"&gt;automated Windows setup&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did some research and found various blog articles and forum entries about how to change the primary display. They (e.g. &lt;a href="https://www.asawicki.info/news_1637_how_to_change_display_mode_using_winapi"&gt;here&lt;/a&gt; and &lt;a href="https://www.codeproject.com/Articles/38903/Set-Primary-Display-ChangeDisplaySettingsEx"&gt;here&lt;/a&gt;) suggest to use the &lt;a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-changedisplaysettingsexw"&gt;ChangeDisplaySettingsEx&lt;/a&gt; call to the user API (&lt;code&gt;winuser.h&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;After some testing, I just couldn't get it to work even though it's supposed to be one simple call to the winuser library. So I used &lt;a href="http://www.rohitab.com/apimonitor"&gt;API Monitor&lt;/a&gt; to see what API calls MultiMonitorTool makes. There are more than 50 calls to the winuser APIs alone. But I eventually found the &lt;code&gt;ChangeDisplaySettingsEx&lt;/code&gt; call and tried to replicate its content exactly (even byte by byte) - still no success.&lt;/p&gt;

&lt;p&gt;After some more research and having a look at random source code snippets from GitHub, I finally found out how to change the primary monitor properly - and it's way more complicated than I could have ever imagined.&lt;/p&gt;

&lt;p&gt;So before diving into the details - here it is: &lt;a href="https://github.com/michidk/displayz"&gt;displays&lt;/a&gt; - a lightweight CLI tool and Rust library to change display properties like primary display, resolution and orientation. I developed the application in Rust using the &lt;a href="https://crates.io/crates/WinSafe"&gt;winsafe crate&lt;/a&gt;, which basically creates a safe wrapper around the Windows API. However, I had to contribute various changes to that crate in order to implement the following API calls.&lt;/p&gt;

&lt;p&gt;I am by no means an expert with regard to the Windows API. There are probably other calls that work as well and simplify some things. But I wanted to keep the tool generic so that it can be used to change the orientation, resolution, primary etc of any amount of monitors connected. So let's get started:&lt;/p&gt;

&lt;p&gt;First, you have to find out the identifier of the display you want to make the new primary. In order to do that, you have to call &lt;a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaydevicesw"&gt;EnumDisplayDevices&lt;/a&gt; multiple times with an increasing counter as a parameter in a loop. Stop looping as soon as a call returns an error.&lt;/p&gt;

&lt;p&gt;After that, you can call &lt;a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaysettingsw"&gt;EnumDisplaySettings&lt;/a&gt; (or the &lt;code&gt;Ex&lt;/code&gt; version) by passing the identifier to retrieve the so-called &lt;a href="https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-devmodew"&gt;DEVMODE structure&lt;/a&gt;. This struct contains information about the display, like position, which is required for the following calls.&lt;/p&gt;

&lt;p&gt;Now we are finally ready to call &lt;code&gt;ChangeDisplaySettingsEx&lt;/code&gt;, right? Turns out you need to call this for every display that is connected and active (I will elaborate on the reason for this later), which means you also gotta call &lt;code&gt;EnumDisplaySettings&lt;/code&gt; for every monitor. Finally, we can execute the program... And nothing happens. &lt;/p&gt;

&lt;p&gt;Turns out you have to call &lt;code&gt;ChangeDisplaySettingsEx&lt;/code&gt; one more time while passing NULL to every parameter, in order to actually "commit" the changes (the monitor will flicker and apply the new properties afterwards).&lt;/p&gt;

&lt;p&gt;MultiMonitorTool does it the same way (other than it issues some additional calls, that are not important for this) - so did we solve the problem? Nope, still doesn't work 😐. After comparing the parameters of other's tools' calls as well as mine', I noticed that the position properties of the &lt;code&gt;ChangeDisplaySettingsEx&lt;/code&gt; call are different.&lt;/p&gt;

&lt;p&gt;And here comes the weird part: The values change depending on which monitor I set as primary. So there is clearly some magic happening, that calculates new position values. This seems to be the difference between the other's approach and my broken one.&lt;/p&gt;

&lt;p&gt;Finally, I found a hint in some Chinese codebase that hinted at &lt;a href="https://docs.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen"&gt;the virtual screen model&lt;/a&gt; that Windows uses. Turns out that the primary display also defines the origin of the coordinate system that the other monitors use when specifying the position. So instead of just passing the position from the &lt;code&gt;EnumDisplaySettings&lt;/code&gt; call, we need to recalculate it accordingly for each active display. To figure that out took me waaay to long - I wish Microsoft had some information about this in their documentation.&lt;/p&gt;

&lt;p&gt;Implementing this is not hard: The new primary display always is located at position (0, 0). The other ones should be moved by the negative position of the old primary display to align them to the new origin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new_display_position = -old_primary_position + old_display_position;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila - it works just fine now. &lt;/p&gt;

&lt;p&gt;🙃&lt;/p&gt;

&lt;p&gt;Feel free to check out the &lt;a href="https://github.com/michidk/displayz/"&gt;code on GitHub&lt;/a&gt; or get the tool from &lt;a href="https://crates.io/crates/displayz"&gt;crates.io&lt;/a&gt; or &lt;a href="https://community.chocolatey.org/packages/displayz"&gt;Chocolatey&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Edit: Apparently there is a new API that simplifies this process (&lt;a href="https://github.com/MicrosoftDocs/windows-driver-docs/blob/staging/windows-driver-docs-pr/display/scaling-the-desktop-image.md"&gt;docs&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>windows</category>
      <category>rust</category>
      <category>tools</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The struggle with SSH key management under Linux</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Thu, 10 Mar 2022 16:20:14 +0000</pubDate>
      <link>https://dev.to/michidk/the-struggle-with-ssh-key-management-under-linux-2ndb</link>
      <guid>https://dev.to/michidk/the-struggle-with-ssh-key-management-under-linux-2ndb</guid>
      <description>&lt;p&gt;&lt;em&gt;Cover Photo by &lt;a href="https://unsplash.com/@contradirony"&gt;Samantha Lam&lt;/a&gt; on &lt;a href="https://unsplash.com/"&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When using tools like &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;ssh&lt;/code&gt; etc. from the command line, reentering the passphrases of your keys can become very tedious rather quickly. This is where key management comes into play: Basically, you want to unlock your key once and keep it ready in your session for the tools to use it until the point where a timeout or system restart occurs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I always just used some zsh ssh-agent plugin or had  &lt;code&gt;eval ssh-agent&lt;/code&gt; in my &lt;code&gt;.bashrc&lt;/code&gt; totally unaware of the fact that this is a very suboptimal solution...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  SSH keys
&lt;/h1&gt;

&lt;p&gt;When connecting to a server using SSH or pushing your changes to a git server you have to authenticate yourself using an SSH key. Git also allows HTTP authentication using a password, &lt;a href="https://www.venafi.com/education-center/ssh/why-is-ssh-security-important"&gt;but you definitely should use  SSH&lt;/a&gt;.  SSH keys also should have a non-empty &lt;a href="https://www.ssh.com/academy/ssh/passphrase"&gt;passphrase&lt;/a&gt; as an additional layer of security. If somebody steals your key (the file on your hard drive), they won't be able to use it without your passphrase.&lt;/p&gt;

&lt;p&gt;You also might want to digitally sign messages and git commits with GPG, which also requires a password-protected key. Now, when actually signing a commit or connecting to a remote server using SSH, you have to enter the passphrase to your key. This is annoying if you have a long secure passphrase and use that very often.&lt;/p&gt;

&lt;h1&gt;
  
  
  ssh-agent
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.ssh.com/academy/ssh/agent"&gt;ssh-agent&lt;/a&gt; solves this problem: It creates a &lt;a href="https://unix.stackexchange.com/questions/16311/what-is-a-socket"&gt;Linux socket&lt;/a&gt; that offers your ssh client access to your keys. It is started with the command &lt;code&gt;ssh-agent&lt;/code&gt;, which returns a path to the socket:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m7C03wnB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1645975825902/4G0ETQdVg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m7C03wnB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1645975825902/4G0ETQdVg.png" alt=" raw `ssh-agent` endraw  showing the path to the socket" width="800" height="268"&gt;&lt;/a&gt;&lt;br&gt;
After adding this path to the &lt;a href="https://linuxize.com/post/how-to-set-and-list-environment-variables-in-linux/"&gt;environment variables&lt;/a&gt; you can use &lt;code&gt;ssh-add &amp;lt;path of your ssh key&amp;gt;&lt;/code&gt; to add your SSH key to the "cache" (you can list your added SSL keys with &lt;code&gt;ssh-add -l&lt;/code&gt;).  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases#auto-launching-ssh-agent-on-git-for-windows"&gt;Most instructions&lt;/a&gt; online, will tell you to add something like &lt;code&gt;eval "$(ssh-agent -s)"&lt;/code&gt; to your &lt;code&gt;.bashrc&lt;/code&gt; file or similar. You might have already noticed, that executing &lt;code&gt;ssh-agent&lt;/code&gt; will give you a new socket every time you execute that command. Meaning with every shell session you now start, you will spawn a new &lt;code&gt;ssh-agent&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--27tsjF9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1645979641207/xDyR8kvXL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--27tsjF9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1645979641207/xDyR8kvXL.png" alt="Commandline output of  raw `ps aux | grep ssh-agent` endraw  showing multiple instances of the  raw `ssh-agent` endraw " width="800" height="315"&gt;&lt;/a&gt;&lt;br&gt;
You will also have to enter your passphrase once per session. There has to be a better way, right? Right?!&lt;/p&gt;

&lt;p&gt;As &lt;a href="https://news.ycombinator.com/item?id=30538109"&gt;ysh7&lt;/a&gt; pointed out, it is also possible to set a custom socket path using the flag &lt;code&gt;-a bind_address&lt;/code&gt; and then set the environment variable &lt;code&gt;SSH_AUTH_SOCK&lt;/code&gt; to that same value. &lt;code&gt;ssh-agent&lt;/code&gt; can then be started as systemd service as described &lt;a href="https://unix.stackexchange.com/questions/339840/how-to-start-and-use-ssh-agent-as-systemd-service"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  keychain, ssh-find-agent, zsh ssh-agent, bash scripts...
&lt;/h1&gt;

&lt;p&gt;Jon Cairns wrote a &lt;a href="http://blog.joncairns.com/2013/12/understanding-ssh-agent-and-ssh-add/"&gt;similar article&lt;/a&gt; about this problem and presented a solution: A script that tries to find and reuse existing &lt;code&gt;ssh-agents&lt;/code&gt;. There are multiple scripts with similar approaches all written in bash: &lt;a href="https://github.com/wwalker/ssh-find-agent"&gt;ssh_find_agent&lt;/a&gt;, &lt;a href="https://github.com/bobsoppe/zsh-ssh-agent/blob/master/ssh-agent.zsh"&gt;zsh-ssh-agent&lt;/a&gt;, and the most popular one: &lt;a href="https://github.com/funtoo/keychain"&gt;keychain&lt;/a&gt;. (And later I also discovered &lt;a href="https://github.com/vodik/envoy"&gt;envoy&lt;/a&gt;). But being bash scripts, they are hard to read, not really fast, and make debugging a hell. I had used &lt;code&gt;keychain&lt;/code&gt; successfully until I encountered a problem that I wasn't able to understand. Also, those tools depend heavily on &lt;code&gt;ssh-agent&lt;/code&gt; and &lt;code&gt;ssh-add&lt;/code&gt; instead of using the socket directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I was ready to implement something similar to &lt;code&gt;keychain&lt;/code&gt; in Rust&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I then actually sat down and implemented a prototype of my SSH/GPG agent manager in Rust, which forced me to really understand the tooling around SSH keys. But there was a problem, I could not solve: Every time I restarted my environment (in my case &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/about"&gt;WSL&lt;/a&gt;), I had to reenter all my passphrases to all the keys even if I wouldn't need them.&lt;/p&gt;
&lt;h1&gt;
  
  
  gpg-agent to the rescue
&lt;/h1&gt;

&lt;p&gt;After some reading through the confusing docs of different (outdated) versions of &lt;code&gt;gpg-agent&lt;/code&gt; (yes not &lt;code&gt;ssh-agent&lt;/code&gt;), I finally found a working solution: Apparently &lt;code&gt;gpg-agent&lt;/code&gt; uses its own socket and works way smarter than &lt;code&gt;ssh-agent&lt;/code&gt;.  Luckily &lt;code&gt;gpg-agent&lt;/code&gt; has support to also manage your ssh keys (and of course also manages your gpg keys)!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I don't fully understand the design decision behind ssh-agent, which prints fairly essential information out as executable code, and doesn't update the current shell with the required environment variables; that just seems a bit bizarre to me. - &lt;a href="http://blog.joncairns.com/2013/12/understanding-ssh-agent-and-ssh-add/"&gt;Jon Cairns&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So how do we use it, then?&lt;br&gt;
First of all, you need &lt;a href="https://gnupg.org/"&gt;GnuPG&lt;/a&gt;, which installs the necessary tools. Sadly there is still no all-in-one version, but GpuPG comes with everything we need.&lt;/p&gt;

&lt;p&gt;Now put the line &lt;code&gt;enable-ssh-support&lt;/code&gt; into your &lt;code&gt;~/.gnupg/gpgagent.conf&lt;/code&gt; (create it, if it does not exist). You can also specify a timeout, by adding the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;## 1-day timeout
&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;-&lt;span class="n"&gt;cache&lt;/span&gt;-&lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt;
&lt;span class="n"&gt;max&lt;/span&gt;-&lt;span class="n"&gt;cache&lt;/span&gt;-&lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the following lines to your &lt;code&gt;.bashrc&lt;/code&gt;, &lt;code&gt;.zshrc&lt;/code&gt; or whatever you are using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GPG_TTY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;tty&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
gpg-connect-agent &lt;span class="nt"&gt;--quiet&lt;/span&gt; updatestartuptty /bye &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SSH_AUTH_SOCK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gpgconf &lt;span class="nt"&gt;--list-dirs&lt;/span&gt; agent-ssh-socket&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GnuPG uses &lt;code&gt;pinentry&lt;/code&gt;, which allows it to create the console UI asking for your password. This &lt;a href="https://www.gnupg.org/documentation/manuals/gnupg/Common-Problems.html"&gt;dialog system requires&lt;/a&gt; the &lt;code&gt;GPG_TTY&lt;/code&gt; environment variable to be pointing at your current tty. The next line starting with &lt;code&gt;gpg-connect-agent&lt;/code&gt; starts the &lt;code&gt;gpg-agent&lt;/code&gt; as a demon in the background if it is not already running and tells it to use your current terminal for those UI dialogs. Since it always outputs "OK", even when we specify &lt;code&gt;--quiet&lt;/code&gt;, we forward the output into &lt;code&gt;/dev/null&lt;/code&gt; to hide it. Finally, we use &lt;code&gt;gpgconf&lt;/code&gt; to tell SSH where the socket of &lt;code&gt;gpg-agent&lt;/code&gt; is located and export it to the environment (so now we use a socket managed by &lt;code&gt;gpg-agent&lt;/code&gt; and not &lt;code&gt;ssh-agent&lt;/code&gt; anymore). &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This is exactly what I have been looking for. I wish I had explored &lt;code&gt;gpg-agent&lt;/code&gt; sooner. Now my shell asks me only once when using specific keys for the first time. The dialogue looks like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cbEC8oT1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1645978161553/NtFjWsubZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cbEC8oT1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1645978161553/NtFjWsubZ.png" alt=" raw `gpg-agent` endraw  asking for my passphrase" width="555" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only downside I see with this approach is that we have to call two commands (&lt;code&gt;gpg-connect-agent&lt;/code&gt; and &lt;code&gt;gpgconf&lt;/code&gt;) every time we launch a new shell. But this is fine, as they are really fast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gpg-connect-agent --quiet updatestartuptty /bye &amp;gt; /dev/null  0.00s user 0.00s system 60% cpu 0.004 total
gpgconf --list-dirs agent-ssh-socket  0.00s user 0.00s system 89% cpu 0.001 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to have a look at my dotfiles, &lt;a href="https://gitlab.com/michidk/dotfiles"&gt;feel free to do so&lt;/a&gt;. Thanks for reading!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ssh</category>
      <category>key</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Automatically deploy websites to Plesk using GitHub Actions or GitLab CI</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Wed, 07 Apr 2021 13:45:43 +0000</pubDate>
      <link>https://dev.to/michidk/automatically-deploy-websites-to-plesk-using-github-actions-or-gitlab-ci-56gj</link>
      <guid>https://dev.to/michidk/automatically-deploy-websites-to-plesk-using-github-actions-or-gitlab-ci-56gj</guid>
      <description>&lt;p&gt;&lt;a href="https://www.plesk.com/"&gt;Plesk&lt;/a&gt; is a commonly used webspace control panel, and thanks to the free &lt;a href="https://www.plesk.com/extensions/git/"&gt;Plesk Git extension&lt;/a&gt;, it is possible to automatically deploy all your changes from any CI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dreaming about a setup where you just push your changes, and they will be immateriality live?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s possible! I use Hugo and NPM to build my static websites and deploy them automatically from GitHub/GitLab to my Plesk webspace.&lt;/p&gt;

&lt;p&gt;So how is this possible? The secret is the &lt;a href="https://www.plesk.com/extensions/git/"&gt;Plesk Git extension&lt;/a&gt;, which can pull any git repository as soon as a webhook is called (this extension has to be installed by your provider, but with most providers, it is available). For that we have to push our built artifact to another branch (similar to how github-pages works), which is then deployed to the webspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Modify your CI
&lt;/h2&gt;

&lt;p&gt;Here it depends what on CI you use. I will provide examples of how it’s done with GitHub Actions and GitLab CI. Generally, the steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an ssh key (deploy key) and put it into a CI variable to grant the CI push access to the repo&lt;/li&gt;
&lt;li&gt;Use an image with git or install git and set up the key&lt;/li&gt;
&lt;li&gt;Create a new branch, e.g., &lt;code&gt;deploy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Clone the deploy branch and delete everything besides the &lt;code&gt;.git&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;Move the built artifact from the previous build step to the cloned (and now empty) folder&lt;/li&gt;
&lt;li&gt;add, commit, push&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Instructions: GitHub
&lt;/h3&gt;

&lt;p&gt;First, generate an ssh key. With GitHub you can then create a deploy key under &lt;code&gt;Settings-&amp;gt;Deploy keys&lt;/code&gt; by inserting the public key. Then make a repository secret named &lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt; under &lt;code&gt;Settings-&amp;gt;Secrets&lt;/code&gt; and insert the private key.&lt;/p&gt;

&lt;p&gt;Your Action definition then might look like this (don’t forget to change the repository URL):&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Instructions: GitLab
&lt;/h3&gt;

&lt;p&gt;First, generate an ssh key. With GitLab you can then create a deploy key under &lt;code&gt;Settings-&amp;gt; Repository-&amp;gt;Deploy keys&lt;/code&gt; by inserting the public key. Then make a protected variable named &lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt; under &lt;code&gt;Settings-&amp;gt;CI/CD-&amp;gt;Variables&lt;/code&gt; and insert the private key.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; CI definition then might look like this (don’t forget to change the repository URL):&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Step 2: Create website in Plesk
&lt;/h2&gt;

&lt;p&gt;Next, create the website in Plesk and add a Git repository. If the repository is private, you have to add the public key given by Plesk to GitHub/GitLab as the deploy key for the SSH connection. Don’t forget to select the deploy branch and correct folder on your webspace! Instruction can be found here.&lt;/p&gt;

&lt;p&gt;Plesk always clones the main branch for the first time before you switched to the other branch. When switching, the old files won’t get deleted, so you might have to clean up once and remove some of the old files from the webspace folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Configure the webhook
&lt;/h2&gt;

&lt;p&gt;Plesk will give you a webhook-URL under the repository settings of the git extension in Plesk. Then configure GitHub/GitLab to call that webhook URL every time a push happens.&lt;/p&gt;

&lt;p&gt;With GitHub and GitLab, this can be done under &lt;code&gt;Settings-&amp;gt;Webhooks&lt;/code&gt; . Select push events and test it.&lt;br&gt;
Step 4: Profit&lt;/p&gt;

&lt;p&gt;That’s it! This should basically work with every CI that has access to your repository and the git Plesk extension.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>github</category>
      <category>gitlab</category>
      <category>plesk</category>
      <category>continuousdeployment</category>
    </item>
    <item>
      <title>Easy Docker LaTeX Setup with vscode and Grammarly</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Thu, 16 Apr 2020 11:39:46 +0000</pubDate>
      <link>https://dev.to/michidk/easy-docker-latex-setup-with-vscode-and-grammarly-4e53</link>
      <guid>https://dev.to/michidk/easy-docker-latex-setup-with-vscode-and-grammarly-4e53</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;LaTeX is a document preparation system, which enables to produce .pdf documents from text and commands for formatting. I use it to write academic research papers and other documents, like my résumé.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I wanted an easy setup for an powerful LaTeX environment using an extensible IDE with Grammar.ly integration, running&lt;br&gt;
in Docker so my system stays clean.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;So the requirements for the setup were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works on Windows and Linux&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt; (vscode) as editor&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://grammarly.com"&gt;Grammar.ly&lt;/a&gt; integration&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://www.tug.org/texlive/"&gt;texlive-full&lt;/a&gt; together with biber and latexmk&lt;/li&gt;
&lt;li&gt;… but without installing them to my system&lt;/li&gt;
&lt;li&gt;Should not require more than a few clicks (or commands) to set up&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Thanks to the awesome vscode extension &lt;a href="https://marketplace.visualstudio.com/items?itemName=James-Yu.lat&amp;lt;br&amp;gt;%0Aex-workshop"&gt;latex-workshop&lt;/a&gt;, which &lt;a href="https://github.com/James-Yu/LaTeX-Workshop/wiki/Install#using-docke&amp;lt;br&amp;gt;%0Ar"&gt;recently added Docker support,&lt;/a&gt; this was quite easy to achieve.&lt;/p&gt;

&lt;p&gt;Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;vscode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install these two vscode extensions: &lt;a href="https://marketplace.visualstudio.com/items?itemName=James-Yu.l&amp;lt;br&amp;gt;%0Aatex-workshop"&gt;latex-workshop,&lt;/a&gt; &lt;a href="https://marketplace.visualstudio.com/items?itemName=znck.grammarly"&gt;Grammarly&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set &lt;code&gt;“latex-workshop.docker.enabled”: true&lt;/code&gt; in your vscode settings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/James-Yu/LaTeX-Workshop/wiki/Compile#building-the-document"&gt;Build your project&lt;/a&gt; with latex workshop&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Additional Setup
&lt;/h2&gt;

&lt;p&gt;If you have a Grammarly account, log in by specifying&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "grammarly.username": "your grammarly email",
    "grammarly.password": "your grammarly password",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also like to add the following configuration values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Grammarly acadmic writing style
    "grammarly.domain": "academic",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // always open pdf preview in new vscode tab
    "latex-workshop.view.pdf.viewer": "tab",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also use the vscode &lt;a href="https://marketplace.visualstudio.com/items?itemName=pkief.material-icon-theme"&gt;Material Icon Theme&lt;/a&gt; plugin in my LaTeX projects. &lt;br&gt;
Which vscode plugins do you use in your LaTeX projects?&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;

</description>
      <category>latex</category>
      <category>docker</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Create a Digital Sign Using a Raspberry Pi (Automated Setup)</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Fri, 10 Apr 2020 11:52:05 +0000</pubDate>
      <link>https://dev.to/michidk/create-a-digital-sign-using-a-raspberry-pi-automated-setup-46e2</link>
      <guid>https://dev.to/michidk/create-a-digital-sign-using-a-raspberry-pi-automated-setup-46e2</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I was asked to create digital signage using a Raspberry Pi 4, displaying upcoming events of a club based in Munich. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bKN5DqWk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1000/1%2Ar__jjWFS0_ip1ywWDZiD1Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bKN5DqWk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1000/1%2Ar__jjWFS0_ip1ywWDZiD1Q.jpeg" alt="The digital sign running in a club in Munich." width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One requirement was, to implement an automated setup so that it can be quickly deployed to new devices.&lt;/p&gt;

&lt;p&gt;After a bit of research, I found &lt;a href="https://dietpi.com/"&gt;DietPi&lt;/a&gt; which is a lightweight OS based on Debian. DietPi is configured using a config file. In there, system settings can be configured. DietPi will then set up your system on the first run using those settings. It is also possible to automatically launch a Chromium instance on a specific URL after starting up.&lt;/p&gt;

&lt;p&gt;Our digital sign is implemented using Vue.js and a simple Bootstrap slider which cycles through images. It is served on a publicly accessible URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;You can adjust the &lt;a href="https://github.com/MichaIng/DietPi/blob/master/config.txt"&gt;standard Raspberry Pi config file&lt;/a&gt; to your needs. In most cases, you want to disable the overscan using &lt;code&gt;disable_overscan=1&lt;/code&gt; and disable the splash screen with &lt;code&gt;disable_splash=1&lt;/code&gt;. If you are using a Raspberry Pi 4, you also want to switch to the new graphics driver by setting &lt;code&gt;dtoverlay=vc4-fkms-v3d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can create a &lt;code&gt;dietpi-wifi.txt&lt;/code&gt; file to specify a WiFi network, the Pi should automatically connect to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# - Entry 0
#   WiFi SSID (Case Sensitive)
aWIFI_SSID[0]='My Wifi'
#   Key options: If no key (open), leave this blank
aWIFI_KEY[0]=''
#   Available options: NONE (no key/open) | WPA-PSK | WEP | WPA-EAP (then use settings below)
aWIFI_KEYMGR[0]='WPA-PSK'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important configuration is done in the &lt;code&gt;dietpi.txt&lt;/code&gt;. Here you want to define a static IP, set an SSH server to launch for maintenance, and set the Pi to wait for a network with &lt;code&gt;CONFIG_BOOT_WAIT_FOR_NETWORK=2&lt;/code&gt;. To enable the automatic setup, define &lt;code&gt;AUTO_SETUP_AUTOMATED=1&lt;/code&gt;, &lt;code&gt;AUTO_SETUP_AUTOSTART_LOGIN_USER=root&lt;/code&gt; and &lt;code&gt;AUTO_SETUP_INSTALL_SOFTWARE_ID=113&lt;/code&gt; (the 113 instructs DietPi to install chromium, see &lt;a href="https://github.com/MichaIng/DietPi/wiki/DietPi-Software-list"&gt;here&lt;/a&gt;). Chromium will be automatically launched in kiosk mode by setting &lt;code&gt;AUTO_SETUP_AUTOSTART_TARGET_INDEX=11&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then configure chromium. E.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SOFTWARE_CHROMIUM_RES_X=1920
SOFTWARE_CHROMIUM_RES_Y=1080
SOFTWARE_CHROMIUM_AUTOSTART_URL=https://google.de
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Custom Automation Script
&lt;/h2&gt;

&lt;p&gt;You can also define a script that runs once at the setup. This is useful if you want to pass custom arguments to the chromium executable and handle other logic as automatically turning off the display during night time.&lt;/p&gt;

&lt;p&gt;To be able to use a custom script, set &lt;code&gt;AUTO_SETUP_AUTOSTART_TARGET_INDEX=7&lt;/code&gt; and &lt;code&gt;AUTO_SETUP_CUSTOM_SCRIPT_EXEC=0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then create a new shell script called &lt;code&gt;Automation_Custom_Script.sh&lt;/code&gt;.&lt;br&gt;
Add the following to that script to automatically run a script during startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo -e "if [[ \"$(tty)\" = \"/dev/tty1\" ]]; then
  . /root/startup.sh
fi
" &amp;gt; /root/.bashrc

echo -e "# turn off screensaver
xset -dpms
xset s off
xset s noblank

# Resolution to use for kiosk mode, should ideally match current system resolution
RES_X=$(grep -m1 '^[[:blank:]]*SOFTWARE_CHROMIUM_RES_X=' /DietPi/dietpi.txt | sed 's/^[^=]*=//')
RES_Y=$(grep -m1 '^[[:blank:]]*SOFTWARE_CHROMIUM_RES_Y=' /DietPi/dietpi.txt | sed 's/^[^=]*=//')

# URL
URL=$(grep -m1 '^[[:blank:]]*SOFTWARE_CHROMIUM_AUTOSTART_URL=' /DietPi/dietpi.txt | sed 's/^[^=]*=//')

# Chromiom args
CHROMIUM_OPTS=\" --no-sandbox --homepage \$URL --app --start-fullscreen --check-for-update-interval=604800 --window-size=\$RES_X,\$RES_Y --app-window-size=\$RES_X,\$RES_Y --window-position=0,0 --incognito --noerrdialogs --disable-infobars \"

# Chromium binary
FP_CHROMIUM=\"$(command -v chromium-browser)\"

xinit \$FP_CHROMIUM \$CHROMIUM_OPTS -- -nocursor
" &amp;gt; /root/startup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands will also create the custom startup script, which will disable the screensaver and run Chromium on the URL specified in the &lt;code&gt;dietpi.txt&lt;/code&gt; as before. The chromium arguments will launch chromium in fullscreen app mode and block any messages from appearing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn the Screen off During Nighttime
&lt;/h2&gt;

&lt;p&gt;To turn the screen off during certain times, add and adjust the following to the &lt;code&gt;Automation_Custom_Script.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "0 1 * * * root DISPLAY=:0 xset dpms force off
55 7 * * * root DISPLAY=:0 xset dpms force on
" &amp;gt;&amp;gt; /etc/crontab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install the Raspberry Pi
&lt;/h2&gt;

&lt;p&gt;So lets set up the Raspberry Pi:&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare the SD card
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Download the DietPi image&lt;/li&gt;
&lt;li&gt;Unzip the image&lt;/li&gt;
&lt;li&gt;Insert SD card&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://www.balena.io/etcher/"&gt;balenaEtcher&lt;/a&gt; to patch the image onto the SD card.&lt;/li&gt;
&lt;li&gt;A) Put the &lt;code&gt;config.txt&lt;/code&gt;, &lt;code&gt;dietpi.txt&lt;/code&gt;, &lt;code&gt;dietpi-wifi.txt&lt;/code&gt; and &lt;code&gt;Automation_Custom_Script.sh&lt;/code&gt; in the root (&lt;code&gt;/&lt;/code&gt;) folder on the SD card. You might want to modify &lt;code&gt;dietpi-wifi.txt&lt;/code&gt;, to adjust the WiFi credentials.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Safely eject the SD card and put it in the Raspberry Pi&lt;/li&gt;
&lt;li&gt;Plug all your cables into the Raspberry Pi (making sure the power cable is last).&lt;/li&gt;
&lt;li&gt;Let the setup run. You have to interact once and accept software terms by pressing enter. The website should open after the setup finished.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>raspberrypi</category>
      <category>digitalsignage</category>
      <category>linux</category>
      <category>infoscreen</category>
    </item>
    <item>
      <title>Two Helpful Tools to Automate Your Windows Setup</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Fri, 10 Apr 2020 09:27:31 +0000</pubDate>
      <link>https://dev.to/michidk/two-helpful-tools-to-automate-your-windows-setup-3998</link>
      <guid>https://dev.to/michidk/two-helpful-tools-to-automate-your-windows-setup-3998</guid>
      <description>&lt;p&gt;I like to have the same configuration &amp;amp; software on all my systems. I also do not want to spend hours on my setup, while resetting my machine or setting up a new one. With Linux, this has always been pretty easy: Just put your dotfiles on a git repo and clone them when you set up a new machine.&lt;/p&gt;

&lt;p&gt;Microsoft is currently working on enhancing the development experience on Windows. They are actively working on a new &lt;a href="https://github.com/microsoft/terminal"&gt;terminal emulator&lt;/a&gt;, a &lt;a href="https://github.com/microsoft/PowerToys/tree/master/src/modules/fancyzones"&gt;window manager&lt;/a&gt;, a way to use &lt;a href="https://ubuntu.com/wsl"&gt;Linux on your Windows machine&lt;/a&gt; and more.&lt;/p&gt;

&lt;p&gt;Two years ago they started working on a &lt;a href="https://github.com/microsoft/windows-dev-box-setup-scripts"&gt;collection of scripts&lt;/a&gt;, to get a new development machine ready with just one click. They have scripts to set up a WSL/Hyper-V, Docker/Kubernetes, NodeJS, Machine Learning, C++ desktop app or Web stack development environment. While those are great, you can also use them as inspiration to create your own collection of scripts.&lt;/p&gt;

&lt;p&gt;We need two tools for our automated setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://chocolatey.org"&gt;Chocolatey&lt;/a&gt;: A package manager for Windows (like &lt;code&gt;apt-get&lt;/code&gt; for Linux), which can install most software with just a command&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://boxstarter.org"&gt;Boxstarter&lt;/a&gt;: A tool to automate the configuration of Windows settings and installation of Chocolatey packages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recommend you to copy one of the scripts from &lt;a href="https://github.com/microsoft/windows-dev-box-setup-scripts"&gt;Microsoft's collection&lt;/a&gt; and adjust it to your needs.&lt;/p&gt;

&lt;p&gt;You can install packages using Chocolatey like so:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fing your package on the &lt;a href="https://chocolatey.org/packages"&gt;online database&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add the following to your Boxstarter script or open a Powershell terminal with admin privileges and execute: &lt;code&gt;choco install &amp;lt;package name&amp;gt; -y&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can change your Windows settings by executing Boxstarter scripts like so:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have a look at the &lt;a href="https://github.com/chocolatey/boxstarter"&gt;Boxstarter repository&lt;/a&gt; and find a script you want to use.&lt;/li&gt;
&lt;li&gt;Add the script to your Boxstarter setup script or open a Powershell terminal with admin privileges and execute it. For example: &lt;code&gt;Set-TaskbarOptions -AlwaysShowIconsOn&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You then can use your script to build a one-click-package by appending your public script/repo/gist to a URL, like so: &lt;code&gt;http://boxstarter.org/package/url?https://raw.githubusercontent.com/GITHUB_DOMAIN/GITHUB_REPO/YOUR_BRANCH/RECIPE_NAME.ps1&lt;/code&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>powershell</category>
      <category>automation</category>
      <category>setup</category>
    </item>
    <item>
      <title>Smartphone-Assisted Virtual Reality</title>
      <dc:creator>Michael Lohr</dc:creator>
      <pubDate>Thu, 09 Apr 2020 15:21:51 +0000</pubDate>
      <link>https://dev.to/michidk/smartphone-assisted-virtual-reality-1elc</link>
      <guid>https://dev.to/michidk/smartphone-assisted-virtual-reality-1elc</guid>
      <description>&lt;h1&gt;
  
  
  Smartphone-Assisted Virtual Reality
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nytAh-zy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2AYch3r-XAMVP_NzvE" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nytAh-zy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2AYch3r-XAMVP_NzvE" alt="Photo by [Lux Interaction](https://medium.com/r/?url=https%3A%2F%2Funsplash.com%3Futm_source%3Dmedium%26utm_medium%3Dreferral) on [Unsplash](https://medium.com/r/?url=https%3A%2F%2Funsplash.com%3Futm_source%3Dmedium%26utm_medium%3Dreferral)" width="800" height="450"&gt;&lt;/a&gt;&lt;em&gt;Photo by &lt;a href="https://medium.com/r/?url=https%3A%2F%2Funsplash.com%3Futm_source%3Dmedium%26utm_medium%3Dreferral"&gt;Lux Interaction&lt;/a&gt; on &lt;a href="https://medium.com/r/?url=https%3A%2F%2Funsplash.com%3Futm_source%3Dmedium%26utm_medium%3Dreferral"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To conclude my bachelor in computer science, I wrote my thesis about using a Smartphone as an interaction device for Virtual Reality. This article is basically a summary of my thesis as a blog. Enjoy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Virtual Reality is an emerging medium that enables presence and interactivity in a three-dimensional space. Conventional input devices like a mouse or a keyboard are made for two-dimensional environments. They require complex movements to complete tasks in a three-dimensional environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vwCKM0KX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2ASPY7g8e7EZCdKHhrIUQWDw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vwCKM0KX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2ASPY7g8e7EZCdKHhrIUQWDw.png" alt="VR controllers by L. Yang on [Steam Community](https://steamcommunity.com/games/250820/announcements/detail/1697188096865619876)" width="686" height="512"&gt;&lt;/a&gt;&lt;em&gt;VR controllers by L. Yang on &lt;a href="https://steamcommunity.com/games/250820/announcements/detail/1697188096865619876"&gt;Steam Community&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is why a variety of different input devices, specially made for Virtual Reality, exist. But most of them are expensive and need complex tracking systems.&lt;/p&gt;

&lt;p&gt;Most people already own a smartphone that they use on a daily basis. Such phones have a variety of different sensors already built-in, feature wireless capabilities, and are able to run custom software. This makes them affordable general-purpose devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiments
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OCQcSnG7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2478/0%2AP4qNFoOsCH5YxqX-" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OCQcSnG7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2478/0%2AP4qNFoOsCH5YxqX-" alt="Screenshot of the laser pointer experiment" width="800" height="523"&gt;&lt;/a&gt;&lt;em&gt;Screenshot of the laser pointer experiment&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three experiments were implemented to demonstrate how the smartphone can help with common interactions when using Virtual Reality applications:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model Viewer:&lt;/strong&gt; An application to view and rotate three-dimensional models. The rotation of the 3D model displayed in VR is synchronized with the Smartphone. To rotate the model, the phone has to be rotated in the wanted direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Laser Pointer:&lt;/strong&gt; A method to select objects or UI elements. A virtual representation of the Smartphone is shown in VR. The rotation is again synced with the real Smartphone. Out of the front of the phone comes a red line (the "laser") which is used to point at user interface (UI) elements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual Keyboard:&lt;/strong&gt; An application to write text on a virtual keyboard. The virtual keyboard is shown in VR. The buttons on it are mapped to the Smartphones's touch screen. A blue circle is used to see where the location, touched on the display, maps to the keyboard in VR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;To get platform independence, I decided to use a web-based stack. All experiments were implemented in WebVR using &lt;a href="https://threejs.org/"&gt;Three.js&lt;/a&gt; and Javascript.&lt;/p&gt;

&lt;p&gt;The networking framework &lt;a href="https://wiki.tum.de/display/infar/Ubi-Interact"&gt;Ubi-Interact&lt;/a&gt;, developed at my university, is used to make the proposed experiments reusable and abstracted from device-specific environments. It connects the PC, running the VR experience, and the Smartphone together. Most of the experiment-specific interaction logic is abstracted into so-called “Ubii Interactions”, which run on a server.&lt;/p&gt;

&lt;p&gt;The client running on the Smartphone was also developed using Javascript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bopJ0zEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A1hhcbl3BeV41j9LUysvpDg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bopJ0zEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A1hhcbl3BeV41j9LUysvpDg.png" alt="Screenshot of the virtual keyboard experiment" width="800" height="522"&gt;&lt;/a&gt;&lt;em&gt;Screenshot of the virtual keyboard experiment&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In order to test the usability of the smartphone as an assistant device for VR, a user study was conducted. In the study, participants had to complete tasks with the three experiments. Also, a &lt;a href="https://www.usability.gov/how-to-and-tools/methods/system-usability-scale.html"&gt;System Usability Scale&lt;/a&gt; (SUS) user study was performed to get feedback from the users.&lt;/p&gt;

&lt;p&gt;A SUS questionnaire asks the participants to rate the following statements regarding the usability of a system from one to five:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I think that I would like to use this system frequently.&lt;br&gt;
I found the system unnecessarily complex.&lt;br&gt;
I thought the system was easy to use.&lt;br&gt;
I think that I would need the support of a technical person to be able to use this system.&lt;br&gt;
I found the various functions in this system were well integrated.&lt;br&gt;
I thought there was too much inconsistency in this system.&lt;br&gt;
I would imagine that most people would learn to use this system very quickly.&lt;br&gt;
I found the system very cumbersome to use.&lt;br&gt;
I felt very confident using the system.&lt;br&gt;
I needed to learn a lot of things before I could get going with this system.&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  To sum up:&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Model Viewer:&lt;/strong&gt; Most participants agreed that this input method is very intuitive and effective to operate.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rJVRdwza--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1018/1%2AchEcUYqCQmJhOlyyNWOGSQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJVRdwza--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1018/1%2AchEcUYqCQmJhOlyyNWOGSQ.png" alt="" width="509" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Laser Pointer:&lt;/strong&gt; This experiment scored the highest amongst the ones presented in this research.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y5Htv0ry--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1016/1%2A7buDm2IYCSnCAm9OyT_INg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y5Htv0ry--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1016/1%2A7buDm2IYCSnCAm9OyT_INg.png" alt="" width="508" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual Keyboard:&lt;/strong&gt; While the model viewer and the laser pointer scenario reached a very high score, the virtual keyboard scored slightly lower but still demonstrates high usability despite its complexity.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Df3lKnC7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1108/1%2Ad3R-hle161lE-2UUcXqjBg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Df3lKnC7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1108/1%2Ad3R-hle161lE-2UUcXqjBg.png" alt="" width="554" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;A lot of feedback was collected during the user study, which can be used to improve these implementations further. Since all experiments are considered “acceptable” according to the System Usability Scale, the Smartphone can indeed be considered a working input device for Virtual Reality.&lt;/p&gt;

&lt;p&gt;If you want to read the full thesis, check out &lt;a href="https://wiki.tum.de/display/infar/BA%3A+Smartphone-Assisted+Virtual+Reality+Using+Ubi-Interact"&gt;this link&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>virtualreality</category>
      <category>smartphone</category>
      <category>research</category>
      <category>humancomputerinteraction</category>
    </item>
  </channel>
</rss>
