<?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: Chris Dawkins</title>
    <description>The latest articles on DEV Community by Chris Dawkins (@siph).</description>
    <link>https://dev.to/siph</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%2F847007%2Fa2d0782c-e328-4998-baf8-7ab827eaf988.png</url>
      <title>DEV Community: Chris Dawkins</title>
      <link>https://dev.to/siph</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siph"/>
    <language>en</language>
    <item>
      <title>Posta: Building a Social Blogging Backend with SurrealDB, Nix, and Nushell</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Tue, 13 May 2025 18:02:55 +0000</pubDate>
      <link>https://dev.to/siph/posta-building-a-social-blogging-backend-with-surrealdb-nix-and-nushell-55kn</link>
      <guid>https://dev.to/siph/posta-building-a-social-blogging-backend-with-surrealdb-nix-and-nushell-55kn</guid>
      <description>&lt;p&gt;This is a quick blog about my experience building a social blogging backend with SurrealDB.  &lt;/p&gt;

&lt;p&gt;I have a personal project that needs a way of simply storing and organizing text posts with the ability to perform full-text-search on these posts. I decided to dive deeper into SurrealDB and, after the obligatory feature creep, built an open source solution that does all this and more!  &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Posta?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/siph/posta" rel="noopener noreferrer"&gt;&lt;code&gt;Posta&lt;/code&gt;&lt;/a&gt; is a social blogging backend built with SurrealDB. The project consists of a SurrealDB schema and uses Nix and Nushell to setup and run tests to assert correct functionality.  &lt;/p&gt;

&lt;p&gt;Some of the features that &lt;code&gt;Posta&lt;/code&gt; offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User signup/signin&lt;/li&gt;
&lt;li&gt;Publish/edit posts&lt;/li&gt;
&lt;li&gt;Leave comments&lt;/li&gt;
&lt;li&gt;Favorite posts&lt;/li&gt;
&lt;li&gt;Subscribe to authors/tags&lt;/li&gt;
&lt;li&gt;Blocking/muting&lt;/li&gt;
&lt;li&gt;Full-text-search&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;h4&gt;
  
  
  SurrealDB
&lt;/h4&gt;

&lt;p&gt;SurrealDB offers several features that make it a strong candidate for building a database backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-Model Database: SurrealDB supports multiple data models, including document-based, graph-based, and relational data.&lt;/li&gt;
&lt;li&gt;Full-Text Indexing: SurrealDB provides full-text indexing capabilities, allowing efficient searching through text content.&lt;/li&gt;
&lt;li&gt;Advanced Access Permissions: SurrealDB offers customizable access permissions at both the table and row levels.&lt;/li&gt;
&lt;li&gt;Live Queries and Real-Time Data Sync: SurrealDB supports live queries and real-time data synchronization, allowing users to receive updates as soon as they occur.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can load &lt;code&gt;posta.surql&lt;/code&gt; into the &lt;a href="https://surrealist.app" rel="noopener noreferrer"&gt;Surrealist&lt;/a&gt; app to explore and learn more about &lt;code&gt;Posta&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Nushell
&lt;/h4&gt;

&lt;p&gt;Nushell has built-in testing, http, and data handling, making it a suitable choice for writing tests.  &lt;/p&gt;

&lt;p&gt;The tests are just scripts that setup the environment, run tests, and then teardown.&lt;/p&gt;

&lt;h4&gt;
  
  
  Nix
&lt;/h4&gt;

&lt;p&gt;Nix is a package manager and build tool that is useful for bundling everything together in a reproducible way.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note about contributing to open source
&lt;/h2&gt;

&lt;p&gt;While doing research for these projects I came across some small bugs in another project. I made the fixes and opened some &lt;a href="https://github.com/ipetkov/crane/pull/824" rel="noopener noreferrer"&gt;PRs&lt;/a&gt; which were accepted.  &lt;/p&gt;

&lt;p&gt;Programmers can be intimidated or unsure of where to start with open source contributions. You can discover many small bugs or typos while reading documentation or source code. These contributions are small but often times are also meaningful. So, don't be afraid of starting small!&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema Design
&lt;/h2&gt;

&lt;p&gt;The schema leverages heavy use of &lt;a href="https://www.datacamp.com/blog/what-is-a-graph-database" rel="noopener noreferrer"&gt;graph-based&lt;/a&gt; design to allow for bi-directional queries, compositional design, and relationship properties.  &lt;/p&gt;

&lt;p&gt;Simple authorization is done using the &lt;a href="https://surrealdb.com/docs/surrealql/statements/define/table#defining-permissions" rel="noopener noreferrer"&gt;&lt;code&gt;PERMISSIONS&lt;/code&gt;&lt;/a&gt; clause with more complex authorization handled using &lt;a href="https://surrealdb.com/docs/surrealql/statements/define/event" rel="noopener noreferrer"&gt;&lt;code&gt;EVENT&lt;/code&gt;&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Below is an visualization of the schema using &lt;a href="https://surrealist.app" rel="noopener noreferrer"&gt;Surrealist&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fjqopueg3alnzf1h31zpu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fjqopueg3alnzf1h31zpu.png" alt="schema" width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Here are a handful of code snippets and usages.&lt;/p&gt;

&lt;h4&gt;
  
  
  New user
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "ns":"posta",
    "db":"posta",
    "ac":"Author",
    "user":"john.doe",
    "email_address":"john.doe@example.com",
    "pass":"123456"
}'&lt;/span&gt; http://localhost:8000/signup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://surrealdb.com/docs/surrealdb/integration/http#signup" rel="noopener noreferrer"&gt;&lt;code&gt;User Signup&lt;/code&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Implementation within &lt;code&gt;Posta&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;DEFINE ACCESS Author ON DATABASE TYPE RECORD
    SIGNIN (
        SELECT * FROM Author WHERE email IS $email AND crypto::argon2::compare(pass, $pass)
    )
    SIGNUP (
        CREATE Author CONTENT {
            name: $name,
            email_address: $email_address,
            pass: crypto::argon2::generate($pass),
        }
    );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://surrealdb.com/docs/surrealql/statements/define/access" rel="noopener noreferrer"&gt;&lt;code&gt;ACCESS&lt;/code&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;h4&gt;
  
  
  New Post
&lt;/h4&gt;

&lt;p&gt;Using a built-in function to make a new post&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn::Post::new($body, $title, $tags)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function definition in schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEFINE FUNCTION fn::Post::new($body: string, $title: string, $tags: array&amp;lt;string&amp;gt;) -&amp;gt; object {
    LET $post = CREATE ONLY Post CONTENT {
        body: $body,
        title: $title,
    } RETURN AFTER;

    FOR $tag IN $tags {
        LET $tag = string::lowercase($tag);
        -- Make new `Tag` if `$tag` doesn't exist
        IF (array::is_empty(SELECT * FROM Tag WHERE tag IS $tag)) {
            CREATE ONLY Tag CONTENT { tag: $tag };
        };

        LET $post_id = $post.id;
        LET $tag_id = (SELECT id FROM Tag WHERE tag IS $tag);

        RELATE $post_id-&amp;gt;tagged-&amp;gt;$tag_id;
    };

    RETURN $post;
} PERMISSIONS FULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://surrealdb.com/docs/surrealql/statements/define/function" rel="noopener noreferrer"&gt;&lt;code&gt;FUNCTION&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Edit Post
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UPDATE $post_id MERGE {body: $body}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;EVENT&lt;/code&gt; that checks for authorization and creates a &lt;code&gt;diff&lt;/code&gt; object when a post is edited.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEFINE EVENT post_edited ON TABLE Post WHEN $event IS "UPDATE" THEN {
    IF array::is_empty(SELECT id FROM published WHERE in IS $auth.id AND out IS $value.id) {
        THROW "Cannot edit unowned resource";
    };

    RELATE ($auth.id)-&amp;gt;edited-&amp;gt;($value.id) CONTENT {
        diff_ops: value::diff($before, $after)
    };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  New Comment
&lt;/h4&gt;

&lt;p&gt;Using &lt;code&gt;SurrealQL&lt;/code&gt; to make a comment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RELATE ($auth.id)-&amp;gt;comment-&amp;gt;$post CONTENT { message: $message }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Text Search
&lt;/h4&gt;

&lt;p&gt;Searching titles&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM Post WHERE title @@ 'SurrealDB';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rank-searching for Post content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT search::score(1) AS score FROM Post
    WHERE body @1@ 'graph-based database'
    ORDER BY score DESC;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://surrealdb.com/docs/surrealql/functions/database/search" rel="noopener noreferrer"&gt;Search functions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Testing is done using Nushell to setup the environment, push queries over http, and assert conditions.  &lt;/p&gt;

&lt;p&gt;Here is a test function that tests comment posting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@test
def comment_post [] {
    let database = surrealdb_setup

    let authors = $database.bind | make_random_authors 2 | each {|it| $it | insert bind ($database | get bind)}

    let post = make_new_post $authors.0 | get result
    let message = random chars

    let comment = {
        query: "RELATE ($auth.id)-&amp;gt;comment-&amp;gt;$post CONTENT { message: $message };"
        args: {
            post: ($post | get id)
            message: $message
        }}
        | send_query $authors.1
        | first

    assert equal $comment.status "OK"

    let comments = {
        query: "SELECT * FROM comment"
        args: {}}
        | send_query $authors.1
        | first

    assert equal $comments.status "OK"
    assert length $comments.result 1
    assert equal ($comments | get result | first | get message) $message

    surrealdb_teardown
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tests get wrapped with nix for CI and other uses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="nv"&gt;checks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;posta-tests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kn"&gt;with&lt;/span&gt; &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;stdenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkDerivation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"posta tests"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./.&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;buildInputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nv"&gt;nushell&lt;/span&gt;
        &lt;span class="nv"&gt;surrealdb&lt;/span&gt;
      &lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nv"&gt;buildPhase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;nushell&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin/nu \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;          --no-config-file \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;          ./tests/mod.nu&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nv"&gt;installPhase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;        touch $out&lt;/span&gt;&lt;span class="err"&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;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Don't be afraid of writing your &lt;em&gt;own&lt;/em&gt; code and your &lt;em&gt;own&lt;/em&gt; bespoke solutions instead of just prompting or using a pre-made solution. You can discover new projects, learn new techniques, gain valuable experience, and meet and collaborate with new people. It's fun to build new things and try new technologies!&lt;/p&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>surrealdb</category>
    </item>
    <item>
      <title>Resume-md: Manage your resume with GitHub and Markdown</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Mon, 15 May 2023 17:10:11 +0000</pubDate>
      <link>https://dev.to/siph/resume-md-manage-your-resume-with-github-and-markdown-25f7</link>
      <guid>https://dev.to/siph/resume-md-manage-your-resume-with-github-and-markdown-25f7</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;This project allows you to write and maintain your resume in markdown. GitHub Actions is used to generate stylized PDF and HTML files based on &lt;code&gt;resume.md&lt;/code&gt; and &lt;code&gt;style.css&lt;/code&gt;. The stylized files are found as outputs in the &lt;code&gt;Releases&lt;/code&gt; section, the HTML file is also deployed as a static website using GitHub Pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category Submission:&lt;/strong&gt; DIY Deployments&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Link:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/siph" rel="noopener noreferrer"&gt;
        siph
      &lt;/a&gt; / &lt;a href="https://github.com/siph/resume-md" rel="noopener noreferrer"&gt;
        resume-md
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Use markdown to generate a stylized resume in PDF and HTML and deploy a static site using GitHub Pages.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Resume-md&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This project allows you to write and maintain your resume in markdown. GitHub
Actions is used to generate stylized PDF and HTML files based on &lt;code&gt;resume.md&lt;/code&gt;
and &lt;code&gt;style.css&lt;/code&gt;. The stylized files are found as outputs in the &lt;code&gt;Releases&lt;/code&gt;
section, the HTML file is also deployed as a static website using GitHub Pages.&lt;/p&gt;
&lt;p&gt;This project is useful for anyone looking to create a professional-looking
resume quickly and easily, and is especially beneficial for those with
technical backgrounds who are familiar with markdown. With this project, you
can focus on the content of your resume rather than worrying about formatting
and deployment.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;GitHub&lt;/h3&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Generate a new project using this repository as a template. &lt;strong&gt;Make sure to include all branches!&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enable Read/Write Workflow permissions under &lt;code&gt;Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;Actions&lt;/code&gt; for Pages deployment.&lt;/li&gt;
&lt;li&gt;Edit the &lt;code&gt;resume.md&lt;/code&gt; file with your resume content using Markdown.&lt;/li&gt;
&lt;li&gt;Commit and push the changes.&lt;/li&gt;
&lt;li&gt;Wait for the GitHub…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/siph/resume-md" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  About
&lt;/h2&gt;

&lt;p&gt;I have seen other projects that provided some of the same features but I wanted something that automatically deployed via GitHub Pages. Unfortunately those projects were not licensed so I didn't feel comfortable forking them. Instead I decided to build my own solution from scratch and used the opportunity to make heavy use of nix throughout the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nix
&lt;/h3&gt;

&lt;p&gt;If you don't know about nix, I wrote &lt;del&gt;some propaganda&lt;/del&gt; an &lt;a href="https://dev.to/siph/hi-there-have-you-heard-the-good-news-about-our-lord-and-savior-nix-o16"&gt;article&lt;/a&gt; introducing some features that nix offers.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Resume-md&lt;/code&gt; uses nix for both dependency management and as a build system. The build process just consists of running a &lt;code&gt;pandoc&lt;/code&gt; command to make the html file and a &lt;code&gt;wkhtmltopdf&lt;/code&gt; command to build the pdf file. This nix derivation just runs those commands and moves the resulting files so that they're accessible in the nix store via &lt;code&gt;/nix/store/${resume-md}/resumes/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nix"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nv"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nv"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;stdenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkDerivation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"resume_md"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./.&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;buildInputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nv"&gt;pandoc&lt;/span&gt;
      &lt;span class="nv"&gt;wkhtmltopdf-bin&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;buildPhase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;      pandoc resume.md \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      -t html -f markdown \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      -c style.css --self-contained \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      -o resume.html&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;      wkhtmltopdf --enable-local-file-access \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      resume.html \&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      resume.pdf&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;    ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nv"&gt;installPhase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;      mkdir -p $out/resume&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      cp resume.* $out/resume/&lt;/span&gt;&lt;span class="err"&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;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;Using nix flakes to manage the dependencies means that each dependency is pinned to a specific commit which results in an extremely reproducible build process. You can run this build process locally to build the files which get placed into the nix store and symlinked to &lt;code&gt;./result&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Actions
&lt;/h3&gt;

&lt;p&gt;The GitHub Actions marketplace is incredibly expansive and includes multiple options for including nix in your Actions. This makes it trivial to leverage all the powerful nix stuff already in place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cachix/install-nix-action@v19&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;github_access_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Resume&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;mkdir out&lt;/span&gt;
        &lt;span class="s"&gt;nix build&lt;/span&gt;
        &lt;span class="s"&gt;cp result/resume/resume.html out/${{ github.actor}}_resume.html&lt;/span&gt;
        &lt;span class="s"&gt;cp result/resume/resume.pdf out/${{ github.actor}}_resume.pdf&lt;/span&gt;
        &lt;span class="s"&gt;cp result/resume/resume.md out/${{ github.actor}}_resume.md&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Store Artifacts&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;resume&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;out/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at how simple this Action is as a result of the nix integration. It just tells nix to build the default package and copies/renames the files to prepend the GitHub username to the build artifacts.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Pages
&lt;/h3&gt;

&lt;p&gt;This build process already outputs an html file so it makes perfect sense to deploy it as a static website using GitHub Pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;generate&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retrieve Artifacts&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;resume&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;out/&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Stage&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;mkdir public&lt;/span&gt;
        &lt;span class="s"&gt;cp out/${{ github.actor }}_resume.html public/index.html&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;peaceiris/actions-gh-pages@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;publish_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./public&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also uses the &lt;code&gt;hub&lt;/code&gt; cli tool, available in any Actions environment, to build a release for the user to download the html, pdf, and original md files. This generates a time-stamp for the release title and adds the resume files, all without needing to manually make and push tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set Tag&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current-datetime&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "CURRENT_DATETIME=$(date +'%Y-%m-%d-%H_%M_%S%z')" &amp;gt;&amp;gt; "$GITHUB_OUTPUT"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Release&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;hub release create ${{ steps.current-datetime.outputs.CURRENT_DATETIME }} \&lt;/span&gt;
        &lt;span class="s"&gt;-m ${{ steps.current-datetime.outputs.CURRENT_DATETIME }} \&lt;/span&gt;
        &lt;span class="s"&gt;-a out/${{ github.actor }}_resume.html \&lt;/span&gt;
        &lt;span class="s"&gt;-a out/${{ github.actor }}_resume.md \&lt;/span&gt;
        &lt;span class="s"&gt;-a out/${{ github.actor }}_resume.pdf&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;GitHub Actions is an awesome automation platform and combined with nix provides and incredible toolkit to build powerful, reproducible and streamlined pipelines.&lt;/p&gt;

</description>
      <category>githubhack23</category>
      <category>github</category>
      <category>career</category>
      <category>programming</category>
    </item>
    <item>
      <title>Writing Polybar Modules in Nushell</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Wed, 26 Apr 2023 14:30:53 +0000</pubDate>
      <link>https://dev.to/siph/writing-polybar-modules-in-nushell-44gm</link>
      <guid>https://dev.to/siph/writing-polybar-modules-in-nushell-44gm</guid>
      <description>&lt;p&gt;&lt;a href="https://www.github.com/polybar/polybar" rel="noopener noreferrer"&gt;Polybar&lt;/a&gt; modules are almost always written in either &lt;code&gt;bash&lt;/code&gt; or &lt;code&gt;python&lt;/code&gt;. &lt;code&gt;Bash&lt;/code&gt; offers an ubiquitous, stable platform and &lt;code&gt;python&lt;/code&gt;, a large ecosystem and much nicer syntax. I want to show how &lt;a href="https://www.github.com/nushell/nushell" rel="noopener noreferrer"&gt;&lt;code&gt;nushell&lt;/code&gt;&lt;/a&gt; can serve as another alternative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Module
&lt;/h2&gt;

&lt;p&gt;The sample module is simple. It queries a GitHub repository for an RSS document and prints how much time has elapsed since the last update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# elapsed.nu


#!/usr/bin/env nu

# Returns how long it has been since a repository received its last update.
def main [
    --user: string,         # GitHub username
    --repository: string    # User repository
] {
    let elapsed = (http get $"https://github.com/($user)/($repository)/releases.atom"
        | from xml
        | get content
        | where { |it| $it.tag == `updated` }
        | $in.content.0.content.0
        | into datetime
        | date humanize)
    print $"($user)/($repository) was updated ($elapsed)."
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not going to get into the syntax of &lt;code&gt;nushell&lt;/code&gt; but here is a summary of what this script does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make a web request.&lt;/li&gt;
&lt;li&gt;Load the RSS response as structured data.&lt;/li&gt;
&lt;li&gt;Filter and traverse the data structure to access values.&lt;/li&gt;
&lt;li&gt;Convert a string into a workable datetime object.&lt;/li&gt;
&lt;li&gt;Determine the amount of elapsed time.&lt;/li&gt;
&lt;li&gt;Print an interpolated message.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This isn't anything that couldn't be done with &lt;code&gt;python&lt;/code&gt; or &lt;code&gt;bash&lt;/code&gt;, but &lt;code&gt;nushell&lt;/code&gt; has some advantages that makes it a compelling option in this instance.&lt;/p&gt;

&lt;p&gt;First, &lt;code&gt;nushell&lt;/code&gt; is able to do all of this without any external dependencies. An http call would need a dependency or significantly more lines in &lt;code&gt;python&lt;/code&gt; and &lt;code&gt;bash&lt;/code&gt; would offload the task to &lt;code&gt;curl&lt;/code&gt;. Deserialization is also non-trivial in &lt;code&gt;python&lt;/code&gt; and &lt;code&gt;bash&lt;/code&gt; even with dependencies.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Nushell&lt;/code&gt; also has a nice syntax, certainly better than &lt;code&gt;bash&lt;/code&gt;. With a functional approach and lots of high-level functionality, powerful operations can be done with relatively small amounts of code.&lt;/p&gt;

&lt;p&gt;There are also constant improvements and changes to the language/shell because &lt;code&gt;nushell&lt;/code&gt; is fairly young and still in very active development. The downside is that this does result in &lt;code&gt;nushell&lt;/code&gt; being far less stable than the others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Module
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Polybar&lt;/code&gt; uses the module the same way it would any other module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[module/elapsed]&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;custom/script&lt;/span&gt;
&lt;span class="py"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;600&lt;/span&gt;
&lt;span class="py"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;~/scripts/elapsed.nu --user nushell --repository nushell&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now its ready to be added to your bar. Here it is in my &lt;a href="https://www.github.com/siph/nix-dotfiles" rel="noopener noreferrer"&gt;config&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%2Fsiph.github.io%2Fblog%2Fassets%2Fimages%2F07%2Fmodule.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%2Fsiph.github.io%2Fblog%2Fassets%2Fimages%2F07%2Fmodule.png" alt="module added"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This isn't a very useful module and only meant as a demonstration, but the weather and clock modules are also written in &lt;code&gt;nushell&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;#clock.nu


#!/usr/bin/env nu

# Get date and time as string with format.
def main [
    --format: string = "%a ● %D ● %r";  # Output string display format. Default: `%a ● %D ● %r`.
] {
    date now | date format $"($format)"
}
&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;#weather.nu


#!/usr/bin/env nu

# Query `wttr.in` for weather report with location and format.
def main [
    --location: string,                         # Can be city name or ICAO code.
    --format: string = "%c%t ● %h ● %w ● %m";   # Optional output string display format. Default: `%c%t ● %h ● %w ● %m`.
] {
    http get $"https://wttr.in/($location)?format=($format)"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;You could really use whatever you wanted to build these simple modules. You could build a &lt;code&gt;java&lt;/code&gt; app and run the jar in the configuration file as long as it prints to stdout. The reason people choose &lt;code&gt;python&lt;/code&gt; or &lt;code&gt;bash&lt;/code&gt; is the same reason they choose them for any other script. &lt;code&gt;Nushell&lt;/code&gt; is nice to work with on both the command line and when building scripts and it makes a great tool to build &lt;code&gt;polybar&lt;/code&gt; modules.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>rust</category>
    </item>
    <item>
      <title>Declare and version control you tmux sessions</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Sun, 16 Apr 2023 20:40:42 +0000</pubDate>
      <link>https://dev.to/siph/declare-and-version-control-you-tmux-sessions-3d2b</link>
      <guid>https://dev.to/siph/declare-and-version-control-you-tmux-sessions-3d2b</guid>
      <description>&lt;p&gt;Anyone who spends a significant amount of time in the command line understands how tedious and cumbersome managing multiple tmux sessions can be. Many programmers find themselves switching between a variety of projects of different languages and sizes on a daily basis. With &lt;a href="https://www.github.com/tmux-python/tmuxp"&gt;&lt;code&gt;tmuxp&lt;/code&gt;&lt;/a&gt; you can declare what a tmux session for a project should look like using json/yaml.&lt;/p&gt;

&lt;p&gt;A simple declaration that fetches from a remote repo and opens &lt;code&gt;neovim&lt;/code&gt; in one window with &lt;code&gt;bottom&lt;/code&gt; running in a second.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ~/example/.tmuxp.yaml&lt;/span&gt;
&lt;span class="na"&gt;session_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Swoll Sesh&lt;/span&gt;
&lt;span class="na"&gt;start_directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/example&lt;/span&gt;
&lt;span class="na"&gt;windows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;window_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nvim&lt;/span&gt;
      &lt;span class="na"&gt;panes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;shell_command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git fetch&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nvim .&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;window_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monitor&lt;/span&gt;
      &lt;span class="na"&gt;panes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;shell_command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;btm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start this session just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmuxp load ~/example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file can then be version controlled, keeping your developer environment organized, consistent and reusable. You can find more examples in the &lt;a href="https://tmuxp.git-pull.com/configuration/examples.html"&gt;documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Combining this with one of the session management plugins for neovim/vim means that your editor will automatically open where you want it.&lt;/p&gt;

&lt;p&gt;This can be even further expanded with &lt;a href="https://nixos.org/"&gt;&lt;code&gt;nix&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://nixos.wiki/wiki/Flakes"&gt;&lt;code&gt;flakes&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://direnv.net/"&gt;&lt;code&gt;direnv&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://github.com/nix-community/nix-direnv"&gt;&lt;code&gt;nix-direnv&lt;/code&gt;&lt;/a&gt; to give you a fully automated and reproducible developer environment complete with the environment variables and dependencies needed to start hacking on a project.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Hi There! Have You Heard The Good News About Our Lord And Savior Nix?</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Tue, 28 Feb 2023 02:30:08 +0000</pubDate>
      <link>https://dev.to/siph/hi-there-have-you-heard-the-good-news-about-our-lord-and-savior-nix-o16</link>
      <guid>https://dev.to/siph/hi-there-have-you-heard-the-good-news-about-our-lord-and-savior-nix-o16</guid>
      <description>&lt;p&gt;It seems like there is a lot of buzz lately around Nix and NixOS. It keeps popping up more and more as a topic of discussion, especially with software developers. However, looking at it's surface and trying to understand what Nix even is can be a struggle. Nix is expansive and knowing what problems it solves and how to even begin to approach the ecosystem can be challenging. This guide is intended to be an introduction and brief exploration into what Nix/NixOS is and how it can benefit you as a developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
What is Nix?

&lt;ul&gt;
&lt;li&gt;Difference Between Nix and NixOS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
System Management

&lt;ul&gt;
&lt;li&gt;
User

&lt;ul&gt;
&lt;li&gt;Home Manager&lt;/li&gt;
&lt;li&gt;Nixos-Generators&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Nixpkgs

&lt;ul&gt;
&lt;li&gt;
Packaging Software

&lt;ul&gt;
&lt;li&gt;Flakes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Development Environment

&lt;ul&gt;
&lt;li&gt;Devshell&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Learning Nix&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Nix?
&lt;/h2&gt;

&lt;p&gt;Nix is a purely functional package manager and operating system deployment tool. It was designed to manage the configuration and installation of software on a variety of operating systems, including Linux, and macOS.&lt;/p&gt;

&lt;p&gt;Nix's distinguishing feature is its functional approach to package management. Instead of the traditional approach of installing packages into a global system directory, Nix packages are built in isolation and stored in a per-user store. This means that multiple versions of a package can coexist on the same system, and dependencies are managed in a consistent and predictable way.&lt;/p&gt;

&lt;p&gt;Here are some reasons why someone might want to use Nix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reproducible builds: Nix ensures that every package build is isolated and uses exactly the same dependencies, making it easy to reproduce builds across different machines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multiple versions: Nix allows you to have multiple versions of the same package installed at the same time, which is useful for development and testing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Declarative package management: Nix uses a declarative language to describe package dependencies and configurations, making it easy to specify exactly what you want installed on a system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rollbacks: Nix allows you to roll back to a previous system configuration if something goes wrong, which can be a lifesaver in production environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cross-platform support: Nix works on a variety of operating systems, so you can use the same package management tool across different machines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Massive package repository: Nix has a huge repository of pre-built packages, which can save time and effort in setting up a new system or developing software. &lt;a href="https://repology.org/repositories/graphs"&gt;See how nixpkgs compares to your package manager.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Difference Between Nix and NixOS
&lt;/h3&gt;

&lt;p&gt;Nix is a package manager for Linux and other Unix-like systems that allows for declarative, reproducible, and isolated software installation and configuration.&lt;/p&gt;

&lt;p&gt;NixOS, on the other hand, is a Linux distribution that uses Nix as its package manager and is built around the principles of declarative system configuration and functional package management. NixOS uses a declarative language (also confusingly called Nix) to describe system configuration, including the installation and configuration of packages, users, network interfaces, and other system components. This makes it easy to reproduce a system's configuration and to roll back to a previous configuration if necessary.&lt;/p&gt;

&lt;p&gt;In other words, while Nix is a package manager that can be used on any Linux or Unix-like system, NixOS is a complete Linux distribution that uses Nix as its package manager and has a unique approach to system configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  System Management
&lt;/h2&gt;

&lt;p&gt;One of the most appealing features of Nix/NixOS that tends to draw new users in is the ability to have your entire system configuration written out and version controlled. This means that your entire fully configured system is ready to deploy anytime, anywhere right from a git repository. This even includes things like SSH or PGP keys by using &lt;a href="https://github.com/ryantm/agenix"&gt;&lt;code&gt;agenix&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  User
&lt;/h3&gt;

&lt;p&gt;Using Nix as a package manager and configuring your home applications with &lt;a href="https://github.com/nix-community/home-manager"&gt;&lt;code&gt;home-manager&lt;/code&gt;&lt;/a&gt; means that your configuration can be deployed outside of NixOS. Nix can reproduce a configuration on Ubuntu, Fedora, etc. as well as macOS. This can allow users to leverage some strengths of Nix without diving all the way into the ecosystem.&lt;/p&gt;

&lt;h4&gt;
  
  
  Home Manager
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/nix-community/home-manager"&gt;&lt;code&gt;Home-manager&lt;/code&gt;&lt;/a&gt; is a tool for managing user-specific configuration files on top of the Nix package manager. It allows users to declaratively manage their user environment, including their shell configuration, editor configuration, and other user-specific settings.&lt;/p&gt;

&lt;p&gt;Home-manager extends the power of NixOS system management to user management letting you declaratively configure your home applications.&lt;/p&gt;

&lt;p&gt;Some examples of what you can do with Home Manager include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuring your shell (e.g. zsh or bash or nushell) with custom settings and plugins&lt;/li&gt;
&lt;li&gt;Installing and configuring your favorite text editor (e.g. Neovim or VSCode) with custom settings and plugins&lt;/li&gt;
&lt;li&gt;Configuring your development tools (e.g. Git or Docker) with custom settings and aliases&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Nixos-Generators
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/nix-community/nixos-generators"&gt;&lt;code&gt;Nixos-generators&lt;/code&gt;&lt;/a&gt; can be used to generate a plethora of formats including ISO. Meaning you can keep a configuration that's specific to your needs and have it handy on a thumb drive.&lt;/p&gt;

&lt;p&gt;You can also build a server configuration and have a cloud image (digitalocean, aws, etc) uploaded and ready to deploy on-demand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server
&lt;/h3&gt;

&lt;p&gt;NixOS really shines when used to build application infrastructure. Things like firewalls, databases, reverse-proxies, and environment variables can all be configured using NixOS declarative module system.&lt;/p&gt;

&lt;p&gt;Using NixOS for server configuration provides several other benefits, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Declarative configuration: NixOS modules provide a declarative way to describe server configuration, allowing you to specify exactly what software should be installed, how it should be configured, and what services should be running on the server. This makes it easy to reproduce server configurations across different machines, and ensures that server configuration is always consistent and predictable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modularity: NixOS modules are designed to be modular, with each module responsible for configuring a specific aspect of the server. This makes it easy to manage complex server configurations, and allows you to reuse configuration across different servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reusability: NixOS modules can be reused across different server configurations, making it easy to share configuration between different projects and teams.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Versioning: NixOS modules are versioned, making it easy to track changes to server configuration over time and to roll back to previous configurations if necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Safety: NixOS modules are designed to be safe, with built-in checks to prevent invalid configurations and to ensure that server configuration is consistent and predictable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Nixpkgs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.github.com/nixos/nixpkgs"&gt;&lt;code&gt;Nixpkgs&lt;/code&gt;&lt;/a&gt; currently has more software packages than any other package repository (excluding the &lt;code&gt;npm&lt;/code&gt; madhouse). The packages also tend to be very fresh and up-to-date thanks to the community and projects like &lt;a href="https://github.com/ryantm/nixpkgs-update"&gt;&lt;code&gt;nixpkgs-update&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Packaging Software
&lt;/h3&gt;

&lt;p&gt;Packaging software for Nix involves creating a Nix derivation, which is an expression that describes how to build and install the software. In this expression you provide a list of dependencies as well as instructions on how the software should be built. There are many helpful tools to make building common project types like cargo, poetry, or go projects much simpler.&lt;/p&gt;

&lt;h4&gt;
  
  
  Flakes
&lt;/h4&gt;

&lt;p&gt;Nix Flakes is a new feature that was introduced in Nix version 2.4. It's a way to make Nix more composable and modular, and to simplify the process of managing and distributing Nix configurations as well as achieving strict reproducibility through a lock file.&lt;/p&gt;

&lt;p&gt;At a high level, Nix Flakes allow you to specify a set of inputs (e.g. Nix packages or other Flakes) and a set of outputs (e.g. a configuration for a specific system or environment). You can then use these inputs to build your outputs, and share your configuration as a single, self-contained unit that can be easily distributed and reused.&lt;/p&gt;

&lt;p&gt;Flakes have created somewhat of a schism in the Nix community although adoption continues to increase as Flakes reduce or eliminate many of the pain-points experienced while working with Nix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Environment
&lt;/h2&gt;

&lt;p&gt;Nix is a developers dream. There is a staggering amount of tooling, utility, and clever mechanisms that can provide enormous amounts of value and productivity to a project. Using Nix, Flakes, and Devshells can eliminate the worry of dependencies and versions inside a developers' environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Devshell
&lt;/h3&gt;

&lt;p&gt;Devshells are a feature of Nix that allow you to create a shell environment with all the dependencies and environment variables needed for a particular project or task. Devshells are useful for developers who work on multiple projects or who need to switch between different programming languages or development environments frequently.&lt;/p&gt;

&lt;p&gt;They're also useful as a means of unifying dependencies and development environments in a consistent and reproducible way. By defining the dependencies and environment in a Nix expression, you can ensure that all developers on your team are using the same versions of libraries and tools, and that the environment can be easily recreated on different machines or platforms.&lt;/p&gt;

&lt;p&gt;This can be further extended into automation environments like CI/CD pipelines and even into production.&lt;/p&gt;

&lt;p&gt;Combined with Flakes, Devshell dependencies can all be pinned to specific versions for extremely consistent reproducibility which virtually eliminates the 'works on my machine' dilemma.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning Nix
&lt;/h2&gt;

&lt;p&gt;Overall, Nix and NixOS offer many benefits for developers, including reproducible builds, isolation, easy package management, and rollback capabilities. These features can help developers be more productive and efficient, and make it easier to collaborate on software projects with others.&lt;/p&gt;

&lt;p&gt;However, Nix has a pretty notorious reputation for being difficult to grasp. The learning curve for Nix and NixOS can be steep, especially for developers who are new to functional programming and declarative configuration management.&lt;/p&gt;

&lt;p&gt;Documentation for NixOS is mostly adequate in 2023 although oftentimes scattered around the internet.&lt;/p&gt;

&lt;p&gt;Despite these challenges, many developers find that the benefits of using Nix and NixOS outweigh the learning curve. Once developers become proficient with Nix and NixOS, they can enjoy the benefits of reproducible builds, isolation, easy package management, and rollback capabilities, among others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/nix-community/awesome-nix"&gt;awesome-nix&lt;/a&gt;: Officially curated list of Nix resources.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nixos/nixpkgs"&gt;nixpkgs&lt;/a&gt;: Software repository for Nix.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nixos.wiki/wiki/Main_Page"&gt;wiki&lt;/a&gt;: Official NixOS wiki.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://discourse.nixos.org/"&gt;discourse&lt;/a&gt;: NixOS community discussion.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://search.nixos.org/packages"&gt;nixos-search&lt;/a&gt;: Search nixpkgs from the web.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>nixos</category>
      <category>programming</category>
      <category>systems</category>
    </item>
    <item>
      <title>Leverage Markdown and GitHub to Manage Your Resume with Resume-md</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Thu, 23 Feb 2023 18:17:14 +0000</pubDate>
      <link>https://dev.to/siph/leverage-markdown-and-github-to-manage-your-resume-with-resume-md-5720</link>
      <guid>https://dev.to/siph/leverage-markdown-and-github-to-manage-your-resume-with-resume-md-5720</guid>
      <description>&lt;p&gt;Many developers are very familiar with benefits of markdown. It has a simple syntax which makes it easy to learn, read, and write. It's also very easy to version control and with a tool like &lt;code&gt;Pandoc&lt;/code&gt;, can be combined with &lt;code&gt;css&lt;/code&gt; to build stylized outputs in multiple formats like PDF and HTML.&lt;/p&gt;

&lt;p&gt;This, combined with GitHub Actions and GitHub Pages can be leveraged to automatically build a stylized PDF for download, and deploy a stylized HTML as a static web page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resume-md
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.github.com/siph/resume-md" rel="noopener noreferrer"&gt;&lt;code&gt;Resume-md&lt;/code&gt;&lt;/a&gt; is a project that automates resume management and does all these things for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  How To Use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Generate a new project using &lt;a href="https://www.github.com/siph/resume-md" rel="noopener noreferrer"&gt;&lt;code&gt;resume-md&lt;/code&gt;&lt;/a&gt; as a template.&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsiph%2Fblog%2Fraw%2Fmaster%2F04%2Ftemplate.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsiph%2Fblog%2Fraw%2Fmaster%2F04%2Ftemplate.png" alt="template" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Make sure to include all branches&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsiph%2Fblog%2Fraw%2Fmaster%2F04%2Fbranches.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsiph%2Fblog%2Fraw%2Fmaster%2F04%2Fbranches.png" alt="branches" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable read/write workflow permissions.&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsiph%2Fblog%2Fraw%2Fmaster%2F04%2Fpermissions.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsiph%2Fblog%2Fraw%2Fmaster%2F04%2Fpermissions.png" alt="permissions" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Edit &lt;code&gt;resume.md&lt;/code&gt; and push commits. Optionally, you can also edit&lt;br&gt;
&lt;code&gt;style.css&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once finished, download your PDF/HTML from the &lt;code&gt;Actions&lt;/code&gt; tab.&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsiph%2Fblog%2Fraw%2Fmaster%2F04%2Fartifacts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fsiph%2Fblog%2Fraw%2Fmaster%2F04%2Fartifacts.png" alt="artifacts" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The HTML is automatically deployed to&lt;br&gt;
&lt;code&gt;https://&amp;lt;your-github-username&amp;gt;.github.io/&amp;lt;repository-name&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This approach has a lot of benefits especially to developers who are already comfortable with markdown and using git. Adding the automated processes using GitHub's tooling is an added layer of convenience and speaks to the power of the platform. This makes it trivial to keep your resume looking good and up to date.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Working with JSON in the command line with jq</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Mon, 26 Sep 2022 14:06:37 +0000</pubDate>
      <link>https://dev.to/siph/working-with-json-in-the-command-line-with-jq-ppj</link>
      <guid>https://dev.to/siph/working-with-json-in-the-command-line-with-jq-ppj</guid>
      <description>&lt;p&gt;JSONs versatility makes it a great choice for many things beyond web communications. Configuration files and logging&lt;br&gt;
files are two things that often are written in, or contain JSON. Working with these files using traditional tools like&lt;br&gt;
&lt;code&gt;awk&lt;/code&gt; and &lt;code&gt;sed&lt;/code&gt; is cumbersome and frustrating. This is where &lt;a href="https://github.com/stedolan/jq"&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/a&gt; comes in.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is &lt;code&gt;jq&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; is a JSON processing tool that uses a very high level, purely functional language to query, mutate, and create&lt;br&gt;
JSON. Utilizing the familiar concepts of pipes, filters, and streams; &lt;code&gt;jq&lt;/code&gt; accepts expressions written in the JSON-like&lt;br&gt;
syntax unique to &lt;code&gt;jq&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; can accept either a file that contains the JSON, or JSON piped through stdin. This allows you to do pretty cool&lt;br&gt;
things like pipe the body of a &lt;code&gt;curl&lt;/code&gt; request into &lt;code&gt;jq&lt;/code&gt;, apply some business logic to derive a new JSON body, and pipe&lt;br&gt;
that body back into another &lt;code&gt;curl&lt;/code&gt; request to send to a remote server. The following examples are going to be ran&lt;br&gt;
against some made-up status style JSON and fed in as a program argument. You can use &lt;a href="https://jqplay.org/"&gt;jqplay&lt;/a&gt;&lt;br&gt;
to try out &lt;code&gt;jq&lt;/code&gt; in the browser and to help build complex commands.&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="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;config.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;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"22.04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dimensions"&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;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1080&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;"devices"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"on"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OK"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OK"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;702&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FAILED"&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;h3&gt;
  
  
  Read Property
&lt;/h3&gt;

&lt;p&gt;Expressions are passed into &lt;code&gt;jq&lt;/code&gt; as a string. Because we are eventually going to be using double quotes to work with&lt;br&gt;
nested strings, we can wrap everything in single quotes to avoid having to escape every quotation.&lt;/p&gt;

&lt;p&gt;The dot operator is used to access the top-level entity within the scope. This gives us access the version property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.version'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="s2"&gt;"20.04"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The truncate tool (&lt;code&gt;tr&lt;/code&gt;) can be used to clean up the quotes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.version'&lt;/span&gt; config.json | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; /&lt;span class="s2"&gt;"

# output:
20.04
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nested properties can be accessed like expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.dimensions.width'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
1920
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Arrays
&lt;/h3&gt;

&lt;p&gt;Arrays can be iterated over by calling the array directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.devices[]'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 22,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"on"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 0,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"OK"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 422,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"off"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 0,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"OK"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 702,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"off"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 99,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"FAILED"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arrays can also be indexed like expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.devices[0]'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 22,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"on"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 0,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"OK"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; also supports slicing of both arrays and strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.devices[:-1]'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 22,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"on"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 0,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"OK"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 422,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"off"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 0,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"OK"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pipes
&lt;/h3&gt;

&lt;p&gt;The real power of &lt;code&gt;jq&lt;/code&gt; starts with the pipe operator. Properties can be selected and then piped into the next expression.&lt;br&gt;
The above example of iterating over an array can also be written using the pipe operator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.devices | .[]'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 22,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"on"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 0,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"OK"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 422,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"off"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 0,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"OK"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 702,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"off"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 99,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"FAILED"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing the &lt;code&gt;state&lt;/code&gt; property of each object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.devices | .[] | .state'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="s2"&gt;"on"&lt;/span&gt;
&lt;span class="s2"&gt;"on"&lt;/span&gt;
&lt;span class="s2"&gt;"off"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Filtering
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; has a number of built-in functions, one of which is &lt;a href="https://stedolan.github.io/jq/manual/#select(boolean_expression)"&gt;&lt;code&gt;select&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;code&gt;select&lt;/code&gt; accepts a boolean expression and returns if the condition evaluates to true.&lt;/p&gt;

&lt;p&gt;Selecting all devices that are disabled and do not have a &lt;code&gt;status&lt;/code&gt; of OK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.devices | .[] | select(.state=="off") | select(.status != "OK")'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 702,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"off"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 99,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"FAILED"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Regex
&lt;/h3&gt;

&lt;p&gt;Instead of comparing strings directly, &lt;code&gt;jq&lt;/code&gt; has several regex functions with the simplest being &lt;code&gt;test&lt;/code&gt;, which returns a&lt;br&gt;
boolean if the input string matches the regular expression. The above filter can be rewritten using &lt;code&gt;test&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.devices | .[] | select(.state|test("off")) | select(.status|test("^((?!OK).)*$"))'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt;: 702,
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"off"&lt;/span&gt;,
    &lt;span class="s2"&gt;"group"&lt;/span&gt;: 99,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"FAILED"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building JSON
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; also lets you mutate and create JSON. Maybe you want to build a new JSON object using only two fields from the&lt;br&gt;
source JSON. You can assign the parent property to a variable so that it is still accessible in the nested scope.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; jq &lt;span class="s1"&gt;'.version as $version | .devices | .[] | {device_id: .id, version: $version}'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 22,
  &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 422,
  &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 702,
  &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can wrap the expression in square brackets to collect the objects into an array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'[.version as $version | .devices | .[] | {device_id: .id, version: $version}]'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 22,
    &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 422,
    &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 702,
    &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets take the &lt;code&gt;dimensions&lt;/code&gt; object and squash it into a new string to add to our objects. This time instead of&lt;br&gt;
assigning a property to a variable, an entire JSON object is created to be referenced later. Parenthesis must be used to&lt;br&gt;
group expressions together. The full expression is starting to get quite long so it's now broken up into multiple lines&lt;br&gt;
and indented to be more readable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; jq &lt;span class="s1"&gt;'
  [
    {
      version: .version,
      dimensions: ((.dimensions.width|tostring) + "x" + (.dimensions.height|tostring))
    }  as $extras
    | .devices
    | .[]
    | {
        device_id: .id,
        version: ($extras|.version),
        dimensions: ($extras|.dimensions)
      }
  ]'&lt;/span&gt; config.json

&lt;span class="c"&gt;# output:&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 22,
    &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;,
    &lt;span class="s2"&gt;"dimensions"&lt;/span&gt;: &lt;span class="s2"&gt;"1920x1080"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 422,
    &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;,
    &lt;span class="s2"&gt;"dimensions"&lt;/span&gt;: &lt;span class="s2"&gt;"1920x1080"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"device_id"&lt;/span&gt;: 702,
    &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"22.04"&lt;/span&gt;,
    &lt;span class="s2"&gt;"dimensions"&lt;/span&gt;: &lt;span class="s2"&gt;"1920x1080"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This guide doesn't cover the full depth of &lt;code&gt;jq&lt;/code&gt; but should be more than enough to get started doing some pretty cool&lt;br&gt;
things. I've only just discovered &lt;code&gt;jq&lt;/code&gt; but I can already see so many ways that it can benefit my workflow. There are&lt;br&gt;
more features included in &lt;code&gt;jq&lt;/code&gt; such as function definitions, conditionals, comparisons, and modification assignment&lt;br&gt;
operators. You can find overviews of all these features and more in the &lt;code&gt;jq&lt;/code&gt; &lt;a href="https://stedolan.github.io/jq/manual/"&gt;manual&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>tooling</category>
      <category>functional</category>
    </item>
    <item>
      <title>Skylunch: API for aviators</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Sun, 28 Aug 2022 17:22:40 +0000</pubDate>
      <link>https://dev.to/siph/skylunch-api-for-aviators-4213</link>
      <guid>https://dev.to/siph/skylunch-api-for-aviators-4213</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Skylunch&lt;/code&gt; is a simple, non-blocking api built using &lt;a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html"&gt;Spring Webflux&lt;/a&gt; that, provided with an airport code, will return a list of restaurants with their ratings and other details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;MEAN/MERN Mavericks&lt;/p&gt;

&lt;h3&gt;
  
  
  Language Used
&lt;/h3&gt;

&lt;p&gt;Kotlin/Spring&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/siph"&gt;
        siph
      &lt;/a&gt; / &lt;a href="https://github.com/siph/skylunch"&gt;
        skylunch
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Api to find restaurants near airports.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/siph/skylunch/actions/workflows/tests.yaml/badge.svg"&gt;&lt;img src="https://github.com/siph/skylunch/actions/workflows/tests.yaml/badge.svg" alt="tests workflow"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/siph/skylunch/actions/workflows/build-image.yaml/badge.svg"&gt;&lt;img src="https://github.com/siph/skylunch/actions/workflows/build-image.yaml/badge.svg" alt="image workflow"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/siph/skylunch../badges/jacoco.svg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MtCNkTnk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/siph/skylunch../badges/jacoco.svg" alt="coverage"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Skylunch&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;The $100 hamburger is a phrase used to describe a recreational flight taken by pilots for the primary purpose of having a meal at a distant airport
The term is often used humorously to describe the high cost of general aviation, but it also represents the freedom and joy of flying for leisure.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Skylunch&lt;/code&gt; is a simple, non-blocking api built using &lt;a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html" rel="nofollow"&gt;Spring Webflux&lt;/a&gt;
that, provided with an airport code, will return a detailed list of restaurants within a configurable radius.&lt;/p&gt;
&lt;p&gt;The purpose of &lt;code&gt;Skylunch&lt;/code&gt; is to help aviators find new and interesting destinations to elevate the enjoyment of their favorite hobby.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Technologies&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Skylunch&lt;/code&gt; is built using the following technologies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/JetBrains/kotlin"&gt;Kotlin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html" rel="nofollow"&gt;Spring Webflux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/redis/redis-om-spring"&gt;Redis OM Spring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spring.io/projects/spring-graphql#overview" rel="nofollow"&gt;Spring for GraphQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Data Storage and Access&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/siph/skylunch./doc/diagram.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VpYlElzw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/siph/skylunch./doc/diagram.png" alt="diagram"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Data is stored with Redis using &lt;a href="https://github.com/redis/redis-om-spring"&gt;Redis OM Spring&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Upon receiving a request, &lt;code&gt;Skylunch&lt;/code&gt; will search the Redis cache/db for a result before…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/siph/skylunch"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Check out &lt;a href="https://redis.io/docs/stack/get-started/clients/#high-level-client-libraries"&gt;Redis OM&lt;/a&gt;, client libraries for working with Redis as a multi-model database.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Use &lt;a href="https://redis.info/redisinsight"&gt;RedisInsight&lt;/a&gt; to visualize your data in Redis.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Sign up for a &lt;a href="https://redis.info/try-free-dev-to"&gt;free Redis database&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redishackathon</category>
      <category>kotlin</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Tiling Window Manager with Rust and Penrose</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Tue, 17 May 2022 17:22:11 +0000</pubDate>
      <link>https://dev.to/siph/building-a-tiling-window-manager-with-rust-and-penrose-5863</link>
      <guid>https://dev.to/siph/building-a-tiling-window-manager-with-rust-and-penrose-5863</guid>
      <description>&lt;p&gt;During the pursuit of increased productivity, many developers strive to eliminate their usage of the mouse as much as possible. The most effective way to eliminate a large percentage of your mouse usage is by switching from a traditional style of window manager to a tiling style window manager.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a tiling-window manager?
&lt;/h2&gt;

&lt;p&gt;Traditional window managers follow a "floating" or "stacking" philosophy. These window managers were originally intended to mimic the familiarity of moving papers around a desk. A newly opened window in a floating-window manager has no regard for the state or visibility of the other opened windows. A tiling-window manager, however, makes the assumption that if a window is open, it should be visible. A newly opened window in a tiling-window manager will be placed in a tile along with the other windows, depending on the chosen layout. The opened windows can then be cycled though, moved, resized, and closed with the use of keyboard bindings. This takes much of the work usually done with the mouse and offloads it to the keyboard thus significantly increasing productivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Penrose?
&lt;/h2&gt;

&lt;p&gt;There are many existing tiling-window managers with &lt;a href="https://i3wm.org/"&gt;i3&lt;/a&gt; probably being the most popular choice for linux systems. These window managers can depend on extensive configuration files or in the case of &lt;a href="https://dwm.suckless.org/"&gt;dwm&lt;/a&gt;, git patching or C programming. &lt;a href="https://github.com/sminez/penrose"&gt;Penrose&lt;/a&gt; takes a different approach in that Penrose is not a window manager. Penrose is a high-level rust &lt;a href="https://docs.rs/penrose/latest/penrose/"&gt;library&lt;/a&gt; that you use to&lt;br&gt;
build your own window manager. This gives us many options for customization while also giving us all the advantages that come with writing rust code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;
&lt;h3&gt;
  
  
  X11
&lt;/h3&gt;

&lt;p&gt;Penrose works for the X11 window management system. This means that your choice of operating system is basically only linux or bsd.&lt;/p&gt;
&lt;h3&gt;
  
  
  Rust
&lt;/h3&gt;

&lt;p&gt;Some familiarity with rust is required. The &lt;a href="https://doc.rust-lang.org/book/"&gt;Rust Book&lt;/a&gt; is the best place&lt;br&gt;
to start.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;To start, we're going to generate a new rust binary project using cargo with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo new mywm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;To build our window manager, we only need two dependencies. A logging library and penrose itself. Our needs are simple so we can just log to stdout. We will add these to our Cargo.toml.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cargo.toml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;penrose&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2"&lt;/span&gt;
&lt;span class="py"&gt;simplelog&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.8"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Styles
&lt;/h2&gt;

&lt;p&gt;Before we start building the main application, lets make some modules which contain the styles that we are going to use. Let's create a styles module, which will make our file tree look 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;mywm
│   Cargo.toml
└───src
│   │   main.rs
│   │   styles.rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  styles.rs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PROFONT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"JetBrainsMono Nerd Font"&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;mod&lt;/span&gt; &lt;span class="n"&gt;colors&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;BLACK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#000000"&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;GREY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#808080"&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;WHITE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#ffffff"&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;PURPLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#a020f0"&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;BLUE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#0000ff"&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;RED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#ff0000"&lt;/span&gt;&lt;span class="p"&gt;;&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;mod&lt;/span&gt; &lt;span class="n"&gt;dimensions&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;HEIGHT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&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;In our styles module we can add our preferred font and submodules for some basic colors and dimensions. We can declare this module directly in our main file along with our intent to use them.&lt;/p&gt;

&lt;h3&gt;
  
  
  main.rs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt; &lt;span class="n"&gt;PROFONT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dimensions&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hooks
&lt;/h2&gt;

&lt;p&gt;Penrose supports the use of hooks to further modify the behavior of our window manager. For our purposes, we are only interested in creating a hook which will execute an external script upon startup. This script will allow us to do things like run feh to set our background, start window-compositors to enable window transparency, and more. We can create the hooks module the same way we created the styles modules, leaving our file tree looking 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;mywm
│   Cargo.toml
└───src
│   │   main.rs
│   │   styles.rs
│   │   hooks.rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  hooks.rs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;penrose&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;core&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
        &lt;span class="nn"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Hook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WindowManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;xconnection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;XConn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;,&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;struct&lt;/span&gt; &lt;span class="n"&gt;StartupScript&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;StartupScript&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;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Into&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;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;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nf"&gt;.into&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;XConn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Hook&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;StartupScript&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&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;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;WindowManager&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;spawn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the entirety of our hooks module. First we declare a struct which holds the path to the script on our system. Then we implement the hook trait for penrose to spawn the process. We can declare this module in the main file the same way we did our styles module.&lt;/p&gt;

&lt;h3&gt;
  
  
  main.rs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Main Application
&lt;/h2&gt;

&lt;p&gt;Now we can move on to implementing the actual application logic. Everything from this point will be added to the main.rs file. To start, we are going to declare a few constant variables which will hold our choice of terminal, application launcher, and the path to our start script.&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;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;TERMINAL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kitty"&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;LAUNCHER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dmenu_run"&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;PATH_TO_START_SCRIPT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"$HOME/.mywm"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace these values with your preferred application choices and the path to your start script. These values could be declared programmatically through the use of something like the &lt;a href="https://docs.rs/clap/latest/clap/"&gt;clap&lt;/a&gt; crate. This would have the benefit of externalizing our configuration, which would allow us to make changes without re-compiling the entire application. That would be beyond the scope of this tutorial. You can, however, find an example of this in my personal build: &lt;a href="https://www.gitlab.com/xsiph/mywm"&gt;HERE&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main Function Return Type
&lt;/h3&gt;

&lt;p&gt;Our main function is going to return a penrose Result to make error handling much simpler.&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;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;penrose&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;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;h3&gt;
  
  
  Logging initialization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;simplelog&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt; &lt;span class="n"&gt;LevelFilter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SimpleLogger&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;SimpleLogger&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="nn"&gt;LevelFilter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;simplelog&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unable to set log level: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&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 are going to use the &lt;a href="https://docs.rs/simplelog/latest/simplelog/"&gt;simplelog&lt;/a&gt; crate to initialize our logger. The SimpleLogger logs to stdout, if we wanted to log to a file we could replace it with WriteLogger.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layouts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;penrose&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;core&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
        &lt;span class="n"&gt;LayoutConf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;side_stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Layout&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="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;side_stack_layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[[]=]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;LayoutConf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;side_stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For our purposes, we are only going to declare a single layout. This layout allows one main window and allocates 60% screen of the real-estate to the main window, and shares the remaining 40% between the other windows. The string is the symbol that will be displayed when the layout is active.&lt;/p&gt;

&lt;h3&gt;
  
  
  Config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;penrose&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.show_bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.top_bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.layouts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;side_stack_layout&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nf"&gt;.focused_border&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PURPLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to build configuration"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This config is very simple. We allocate space for a top bar, add our layouts, and choose a border color which will appear around the active window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Top-Bar
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;penrose&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
        &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;dwm_bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nn"&gt;xcb&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;XcbDraw&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;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PROFONT&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;point_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WHITE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BLACK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;empty_ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GREY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;draw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;XcbDraw&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dwm_bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HEIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PURPLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;empty_ws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="nf"&gt;.workspaces&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.clone&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;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could use something like &lt;a href="https://github.com/polybar/polybar"&gt;polybar&lt;/a&gt; to build a powerful and sophisticated top-bar for our system. However, for this example we are going to use the built-in dwm_bar which mimics the bar that can be found in dwm. What's happening here is pretty straight-forward. First we populate the styling struct, and then we plug these values into the dwm_bar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keybindings
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;penrose&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;core&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
        &lt;span class="nn"&gt;ring&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
            &lt;span class="n"&gt;Forward&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Backward&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nn"&gt;data_types&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Change&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
            &lt;span class="n"&gt;More&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Less&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nn"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;index_selectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Selector&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;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key_bindings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;gen_keybindings!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Program launchers&lt;/span&gt;
        &lt;span class="s"&gt;"M-p"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_external!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LAUNCHER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-Return"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_external!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TERMINAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Exit Penrose (important to remember this one!)&lt;/span&gt;
        &lt;span class="s"&gt;"M-A-C-Escape"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// client management&lt;/span&gt;
        &lt;span class="s"&gt;"M-j"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cycle_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Forward&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-k"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cycle_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backward&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-S-j"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drag_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Forward&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-S-k"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drag_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backward&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-f"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toggle_client_fullscreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;Selector&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Focused&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-c"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kill_client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// workspace management&lt;/span&gt;
        &lt;span class="s"&gt;"M-Tab"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toggle_workspace&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-A-period"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cycle_workspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Forward&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-A-comma"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cycle_workspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backward&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Layout management&lt;/span&gt;
        &lt;span class="s"&gt;"M-grave"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cycle_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Forward&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-S-grave"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cycle_layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backward&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-A-Up"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_max_main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;More&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-A-Down"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_max_main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Less&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-l"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_main_ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;More&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="s"&gt;"M-h"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;run_internal!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_main_ratio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Less&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"5"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nf"&gt;index_selectors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="s"&gt;"M-{}"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;focus_workspace&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="s"&gt;"M-S-{}"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;client_to_workspace&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REF&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Penrose includes a helpful macro that allows us to quickly set our keybindings. The 'M' key is the meta key aka Windows key. We also label our workspaces here. We are only declaring 5, but you could use any arbitrary number of workspaces. We also label our workspaces with numbers, but they could be labeled using icons or emojis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hooks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;penrose&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;core&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
        &lt;span class="nn"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Hooks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;XcbConnection&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;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hooks&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;XcbConnection&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;StartupScript&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATH_TO_START_SCRIPT&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;Here we create a vector to hold our hooks. We only have two hooks, the top-bar, and the start script we declared earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;penrose&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;new_xcb_backed_window_manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;logging_error_handler&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;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;wm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_xcb_backed_window_manager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;logging_error_handler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;wm&lt;/span&gt;&lt;span class="nf"&gt;.grab_keys_and_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_bindings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;map!&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All that is left now is to build it and run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Compiling and Running
&lt;/h3&gt;

&lt;p&gt;Compilation is as simple as running the cargo 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;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have a binary, how do we run it? We could use &lt;a href="https://wiki.archlinux.org/title/Xinit"&gt;xinit&lt;/a&gt; to launch a session directly from a tty. Instead, if you already have a login manager installed, you can move the binary to the /usr/bin/ directory and make a mywm.desktop file that looks 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;[Desktop Entry]
Encoding=UTF-8
Name=Mywm
Comment=Tiling Window Manager
Exec=mywm
Type=Xsession
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place the .desktop file in /usr/share/xsessions/ directory, and you will be able to select mywm upon login.&lt;/p&gt;

&lt;h3&gt;
  
  
  .mywm Hook Script
&lt;/h3&gt;

&lt;p&gt;We built a hook that would run our script on startup. The script can be used to do many things, but the most common would probably be to set your background.&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;#!/bin/sh&lt;/span&gt;
 feh &lt;span class="nt"&gt;--no-fehbg&lt;/span&gt; &lt;span class="nt"&gt;--bg-scale&lt;/span&gt; &lt;span class="s1"&gt;'$HOME/Pictures/background.png'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just make sure that .mywm has executable privileges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jetbrains IDE
&lt;/h2&gt;

&lt;p&gt;Intellij along with other Jetbrains IDEs can have trouble when running under a tiling-window manager. To solve this problem, you need to export a variable for the JVM that runs the IDE to use:&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;_JAVA_AWT_WM_NONREPARENTING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The best place to put this is in an .env file like .zshenv, if zsh is your default shell.&lt;/p&gt;

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

&lt;p&gt;Building your own window manager can be a very daunting undertaking. With tools like Penrose, much of the complexities involved are hidden behind helpful libraries. This particular build only scratches the surface of what can be accomplished. The complete code for this project can be found on my &lt;a href="https://gitlab.com/xsiph/MYWM/-/tree/article-version"&gt;gitlab&lt;/a&gt; alongside my actual &lt;a href="https://gitlab.com/xsiph/MYWM/"&gt;build&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>linux</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Easy and Advanced Neovim Setup with LunarVim</title>
      <dc:creator>Chris Dawkins</dc:creator>
      <pubDate>Fri, 15 Apr 2022 21:48:49 +0000</pubDate>
      <link>https://dev.to/siph/easy-and-advanced-neovim-setup-with-lunarvim-4n18</link>
      <guid>https://dev.to/siph/easy-and-advanced-neovim-setup-with-lunarvim-4n18</guid>
      <description>&lt;p&gt;Neovim is a lightweight and powerful text editor that can be a valuable addition to any developers toolbox.&lt;br&gt;
Highly configurable and extensible, Neovim can be transformed from a simple text-editor into a full-featured development environment.&lt;br&gt;
Configuring Neovim, however, can be a daunting and difficult task. Even maintaining a Neovim configuration can be a frustrating task in itself.&lt;br&gt;
This is where LunarVim comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is LunarVim?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/LunarVim/LunarVim" rel="noopener noreferrer"&gt;LunarVim&lt;/a&gt; is a project that aims to provide an easy and powerful configuration for Neovim.&lt;br&gt;
Some useful features included in LunarVim by default are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Language server provider (lsp) support.&lt;/li&gt;
&lt;li&gt;Automatically installed language servers based on opened file extensions.&lt;/li&gt;
&lt;li&gt;Treesitter support.&lt;/li&gt;
&lt;li&gt;File tree browser.&lt;/li&gt;
&lt;li&gt;Fuzzy searching.&lt;/li&gt;
&lt;li&gt;Multiple default color themes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installing LunarVim
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;Upon reading the &lt;a href="https://www.lunarvim.org/01-installing.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;, we can see that LunarVim has a few dependencies.&lt;br&gt;
On Ubuntu, node and npm can be installed by the package manager using the command:   &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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nodejs npm


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To install cargo we can use &lt;a href="https://rustup.rs/" rel="noopener noreferrer"&gt;rustup&lt;/a&gt; by running the command: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; https://sh.rustup.rs | sh


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To avoid any permission issues, we can change the default directory that npm uses to store packages.&lt;br&gt;
To change the default directory we can follow &lt;a href="https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally" rel="noopener noreferrer"&gt;the guide&lt;/a&gt; in the npm docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Neovim
&lt;/h3&gt;

&lt;p&gt;LunarVim requires version 0.5+ of Neovim to function.&lt;br&gt;
A helper script is included in the LunarVim documentation to quickly install the correct version:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/LunarVim/LunarVim/rolling/utils/installer/install-neovim-from-release&lt;span class="o"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Once that has been done, we are ready to install LunarVim.&lt;/p&gt;

&lt;p&gt;I prefer installing from the rolling branch because the stable branch can feel neglected.&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;LV_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rolling bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/lunarvim/lunarvim/rolling/utils/installer/install.sh&lt;span class="o"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After installation we can finally run LunarVim with the command: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

lvim


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And we are greeted with the default welcome screen.&lt;br&gt;&lt;br&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%2Fa01hnjlza1c48sklgvev.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%2Fa01hnjlza1c48sklgvev.png" alt="Default look"&gt;&lt;/a&gt;   &lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring LunarVim
&lt;/h2&gt;

&lt;p&gt;The default configurations are perfectly usable and capable of serious development right out of the gate.&lt;br&gt;
However, with some simple changes we can make it more closely fit our preferences.&lt;/p&gt;

&lt;p&gt;If we look in our user configurations files (~/.config/lvim/), we can see that LunarVim has generated a default configuration file.&lt;br&gt;
After opening the file, LunarVim automatically installs the language server for lua, providing us with auto-completion and linting which makes working with the configuration file much easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing the Leader Key and Theme
&lt;/h3&gt;

&lt;p&gt;By default LunarVim uses the spacebar as its leader key. I, however, prefer to use the comma key.&lt;br&gt;
This is easily done by editing or adding the leader key entry:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="n"&gt;lvim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;","&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The configuration contains a 'plugins' section that has been commented out by default. If we uncomment this section we gain access to a new color theme 'tokyonight'.&lt;br&gt;&lt;br&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%2F3rxyocpbhsecmwwwckgg.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%2F3rxyocpbhsecmwwwckgg.png" alt="uncomment plugins"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Uncomment the plugins section and modify the theme entry to use tokyonight:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="n"&gt;lvim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colorscheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tokyonight"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After a restart we can see our new theme.&lt;br&gt;&lt;br&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%2Ftu31jdgnnrw4st7lt3mg.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%2Ftu31jdgnnrw4st7lt3mg.png" alt="tokyonight theme"&gt;&lt;/a&gt;   &lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Plugins
&lt;/h3&gt;

&lt;p&gt;As you can probably tell, installing plugins can be as simple as adding the repository name to the lvim.plugins entry.&lt;br&gt;
Let try adding a new plugin that will give us access to the dracula theme:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="n"&gt;lvim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&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="s2"&gt;"folke/tokyonight.nvim"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"dracula/vim"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"folke/trouble.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"TroubleToggle"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If the plugins do not automatically install, we can install them manually by pressing colon(':') and running the command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

PackerInstall


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Upon restart we can see our new theme in the list.&lt;br&gt;&lt;br&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%2F9dh9ln540plaxv7gn5vt.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%2F9dh9ln540plaxv7gn5vt.png" alt="darcula theme added"&gt;&lt;/a&gt;   &lt;/p&gt;

&lt;h2&gt;
  
  
  Reinstall Script
&lt;/h2&gt;

&lt;p&gt;LunarVim has a tendency to break on occasion. A reinstallation seems to always fix these problems so I like to keep a script to handle the reinstallation and configuration file management.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# Unstow dotfiles&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Unstowing dotfiles...'&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.dotfiles &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; stow &lt;span class="nt"&gt;--delete&lt;/span&gt; lvim&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# Uninstall&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Running uninstall script...'&lt;/span&gt;&lt;br&gt;
bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;a href="https://raw.githubusercontent.com/lunarvim/lunarvim/master/utils/installer/uninstall.sh" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/lunarvim/lunarvim/master/utils/installer/uninstall.sh&lt;/a&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# Install neovim&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Installing neovim...'&lt;/span&gt;&lt;br&gt;
bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;a href="https://raw.githubusercontent.com/LunarVim/LunarVim/rolling/utils/installer/install-neovim-from-release" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/LunarVim/LunarVim/rolling/utils/installer/install-neovim-from-release&lt;/a&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# Install lunarvim&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Installing lunarvim...'&lt;/span&gt;&lt;br&gt;
&lt;span class="nv"&gt;LV_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rolling bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;a href="https://raw.githubusercontent.com/lunarvim/lunarvim/rolling/utils/installer/install.sh" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/lunarvim/lunarvim/rolling/utils/installer/install.sh&lt;/a&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c"&gt;# Stow dotfiles&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Stowing dotfiles...'&lt;/span&gt;&lt;br&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.config/lvim&lt;br&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.dotfiles &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; stow lvim&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;LunarVim isn't the only project that maintains a sophisticated Neovim configuration. It is, however, actively developed and comes with many sane defaults which most people put into their Neovim configurations anyway.&lt;br&gt;
It can be a great option for Neovim beginners or seasoned veterans who are tired of maintaining a fragile and frustrating configuration file.&lt;br&gt;
LunarVim can also be used outside of development. This article was written using LunarVim with full spell-checking and grammar-checking support.&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%2Flaff1gy35dypq5e3p94u.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%2Flaff1gy35dypq5e3p94u.png" alt="Article screenshot"&gt;&lt;/a&gt;&lt;br&gt;
My LunarVim and other configurations can be found on &lt;a href="https://www.gitlab.com/xsiph/dotfiles" rel="noopener noreferrer"&gt;my gitlab&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vim</category>
      <category>programming</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
