{"@attributes":{"version":"2.0"},"channel":{"title":"Shorts on Francis Sunday","link":"https:\/\/codehakase.com\/shorts\/","description":"Recent content in Shorts on Francis Sunday","generator":"Hugo","language":"en-us","lastBuildDate":"Sat, 14 Jun 2025 00:00:00 +0000","item":[{"title":"Parsing, Not Guessing","link":"https:\/\/codehakase.com\/shorts\/parsing-not-guessing\/","pubDate":"Sat, 14 Jun 2025 00:00:00 +0000","guid":"https:\/\/codehakase.com\/shorts\/parsing-not-guessing\/","description":"<p>Markdown rendering in the terminal is usually done with string replacements or regex. This doesn\u2019t scale. Goldmark exposes a proper AST. Each node has type, position, and content. Rendering becomes walking the tree:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-go\" data-lang=\"go\"><span style=\"display:flex;\"><span><span style=\"color:#177500\">\/\/ render renders the AST node to the writer<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">func<\/span> (<span style=\"color:#000\">tr<\/span> <span style=\"color:#000\">*<\/span><span style=\"color:#000\">terminalRenderer<\/span>) <span style=\"color:#000\">render<\/span>(<span style=\"color:#000\">w<\/span> <span style=\"color:#000\">io<\/span>.<span style=\"color:#000\">Writer<\/span>, <span style=\"color:#000\">source<\/span> []<span style=\"color:#a90d91\">byte<\/span>, <span style=\"color:#000\">node<\/span> <span style=\"color:#000\">ast<\/span>.<span style=\"color:#000\">Node<\/span>) <span style=\"color:#a90d91\">error<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>\t<span style=\"color:#a90d91\">return<\/span> <span style=\"color:#000\">ast<\/span>.<span style=\"color:#000\">Walk<\/span>(<span style=\"color:#000\">node<\/span>, <span style=\"color:#a90d91\">func<\/span>(<span style=\"color:#000\">node<\/span> <span style=\"color:#000\">ast<\/span>.<span style=\"color:#000\">Node<\/span>, <span style=\"color:#000\">entering<\/span> <span style=\"color:#a90d91\">bool<\/span>) (<span style=\"color:#000\">ast<\/span>.<span style=\"color:#000\">WalkStatus<\/span>, <span style=\"color:#a90d91\">error<\/span>) {\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#000\">err<\/span> <span style=\"color:#000\">:=<\/span> <span style=\"color:#000\">tr<\/span>.<span style=\"color:#000\">renderNode<\/span>(<span style=\"color:#000\">w<\/span>, <span style=\"color:#000\">source<\/span>, <span style=\"color:#000\">node<\/span>, <span style=\"color:#000\">entering<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#a90d91\">if<\/span> <span style=\"color:#000\">err<\/span> <span style=\"color:#000\">!=<\/span> <span style=\"color:#a90d91\">nil<\/span> {\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t\t<span style=\"color:#a90d91\">return<\/span> <span style=\"color:#000\">ast<\/span>.<span style=\"color:#000\">WalkStop<\/span>, <span style=\"color:#000\">err<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t}\n<\/span><\/span><span style=\"display:flex;\"><span>\t\t<span style=\"color:#a90d91\">return<\/span> <span style=\"color:#000\">ast<\/span>.<span style=\"color:#000\">WalkContinue<\/span>, <span style=\"color:#a90d91\">nil<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\t})\n<\/span><\/span><span style=\"display:flex;\"><span>}\n<\/span><\/span><\/code><\/pre><\/div><p>This is better. You don\u2019t need to guess whether a * means emphasis or a list. You know.<\/p>"},{"title":"import \"reflect\"","link":"https:\/\/codehakase.com\/shorts\/import-reflect\/","pubDate":"Tue, 10 Jun 2025 00:00:00 +0000","guid":"https:\/\/codehakase.com\/shorts\/import-reflect\/","description":"<p>An attempt at a concise explanation of Go\u2019s <code>reflect<\/code> library.<\/p>\n<p>The <code>reflect<\/code> package implements run-time reflection, allowing a program to check, amend and create arbitrary values of all kinds during implementation. Traditionally, Go is static, types are known and verified at compile time. The <code>reflect<\/code> package allows you to escape from this static world. It operates primarily on two concepts: <code>reflect.Type<\/code>, which represents the static properties of a type (like its name, methods, and struct fields), and <code>reflect.Value<\/code>, which holds the actual underlying data and allows for dynamic reading and writing.<\/p>"},{"title":"TTYs","link":"https:\/\/codehakase.com\/shorts\/ttys\/","pubDate":"Tue, 13 May 2025 00:00:00 +0000","guid":"https:\/\/codehakase.com\/shorts\/ttys\/","description":"<p>Today I learned about the <code>tty<\/code> command. It returns the current user&rsquo;s terminal name.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-man\" data-lang=\"man\"><span style=\"display:flex;\"><span>TTY(1) General Commands Manual TTY(1)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>NAME\n<\/span><\/span><span style=\"display:flex;\"><span> tty \u2013 return user&#39;s terminal name\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>SYNOPSIS\n<\/span><\/span><span style=\"display:flex;\"><span> tty [-s]\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>DESCRIPTION\n<\/span><\/span><span style=\"display:flex;\"><span> The tty utility writes the name of the terminal attached to standard\n<\/span><\/span><span style=\"display:flex;\"><span> input to standard output. The name that is written is the string\n<\/span><\/span><span style=\"display:flex;\"><span> returned by ttyname(3). If the standard input is not a terminal, the\n<\/span><\/span><span style=\"display:flex;\"><span> message \u201cnot a tty\u201d is written. The options are as follows:\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span> -s Do not write the terminal name; only the exit status is affected\n<\/span><\/span><span style=\"display:flex;\"><span> when this option is specified. The -s option is deprecated in\n<\/span><\/span><span style=\"display:flex;\"><span> favor of the \u201ctest -t 0\u201d command.\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span>EXIT STATUS\n<\/span><\/span><span style=\"display:flex;\"><span> The tty utility exits 0 if the standard input is a terminal, 1 if the\n<\/span><\/span><span style=\"display:flex;\"><span> standard input is not a terminal, and &gt;1 if an error occurs.\n<\/span><\/span><\/code><\/pre><\/div><p>One cool thing I found was if you get the tty of a terminal tab, you can push text to it. For instance, running <code>echo &quot;some text&quot; &gt; \/dev\/tty-{num}<\/code> will push the text <code>some text<\/code> to the terminal with the tty.\n<img src=\"https:\/\/blob.codehakase.com\/static\/images\/shorts\/tty\/tty.gif\" alt=\"TTY sample\"><\/p>"},{"title":"GADTs for Type-Level Domain Logic","link":"https:\/\/codehakase.com\/shorts\/gadts-for-type-level-domain-logic\/","pubDate":"Wed, 07 May 2025 00:00:00 +0000","guid":"https:\/\/codehakase.com\/shorts\/gadts-for-type-level-domain-logic\/","description":"<p>I&rsquo;ve been leveraging Generalized Algebraic Data Types (GADTs) in Haskell and found them exceptionally powerful for encoding domain-specific rules and state transitions directly into the type system. This is particularly useful for defining operations that are based on explicitly tagged types or state, and behave differently.<\/p>\n<p>Consider a scenario with distinct processing steps, each associated with specific data:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-haskell\" data-lang=\"haskell\"><span style=\"display:flex;\"><span><span style=\"color:#177500\">{-# LANGUAGE GADTs #-}<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">module<\/span> <span style=\"color:#000\">StepProcessor<\/span> <span style=\"color:#a90d91\">where<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">data<\/span> <span style=\"color:#a90d91\">AuthData<\/span> <span style=\"color:#000\">=<\/span> <span style=\"color:#a90d91\">AuthData<\/span> \n<\/span><\/span><span style=\"display:flex;\"><span> { <span style=\"color:#000\">userId<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">String<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> , <span style=\"color:#000\">token<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">String<\/span> \n<\/span><\/span><span style=\"display:flex;\"><span> } \n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#a90d91\">deriving<\/span> (<span style=\"color:#a90d91\">Show<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">data<\/span> <span style=\"color:#a90d91\">ReviewData<\/span> <span style=\"color:#000\">=<\/span> <span style=\"color:#a90d91\">ReviewData<\/span> \n<\/span><\/span><span style=\"display:flex;\"><span> { <span style=\"color:#000\">documentId<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">Int<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> , <span style=\"color:#000\">comments<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">String<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> }\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#a90d91\">deriving<\/span> (<span style=\"color:#a90d91\">Show<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">data<\/span> <span style=\"color:#a90d91\">Step<\/span> <span style=\"color:#000\">a<\/span> <span style=\"color:#a90d91\">where<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#a90d91\">AuthStep<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">AuthData<\/span> <span style=\"color:#000\">-&gt;<\/span> <span style=\"color:#a90d91\">Step<\/span> <span style=\"color:#a90d91\">AuthData<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#a90d91\">ReviewStep<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">ReviewData<\/span> <span style=\"color:#000\">-&gt;<\/span> <span style=\"color:#a90d91\">Step<\/span> <span style=\"color:#a90d91\">ReviewData<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">validateAuth<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">AuthData<\/span> <span style=\"color:#000\">-&gt;<\/span> <span style=\"color:#a90d91\">IO<\/span> <span style=\"color:#a90d91\">()<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">validateAuth<\/span> <span style=\"color:#000\">ad<\/span> <span style=\"color:#000\">=<\/span> <span style=\"color:#000\">putStrLn<\/span> <span style=\"color:#000\">$<\/span> <span style=\"color:#c41a16\">&#34;Validating auth for user: &#34;<\/span> <span style=\"color:#000\">++<\/span> <span style=\"color:#000\">userId<\/span> <span style=\"color:#000\">ad<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">runReview<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">ReviewData<\/span> <span style=\"color:#000\">-&gt;<\/span> <span style=\"color:#a90d91\">IO<\/span> <span style=\"color:#a90d91\">()<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">runReview<\/span> <span style=\"color:#000\">rd<\/span> <span style=\"color:#000\">=<\/span> <span style=\"color:#000\">putStrLn<\/span> <span style=\"color:#000\">$<\/span> <span style=\"color:#c41a16\">&#34;Running review for doc: &#34;<\/span> <span style=\"color:#000\">++<\/span> <span style=\"color:#000\">show<\/span> (<span style=\"color:#000\">documentId<\/span> <span style=\"color:#000\">rd<\/span>) <span style=\"color:#000\">++<\/span> <span style=\"color:#c41a16\">&#34; with comments: &#34;<\/span> <span style=\"color:#000\">++<\/span> <span style=\"color:#000\">comments<\/span> <span style=\"color:#000\">rd<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">processStep<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">Step<\/span> <span style=\"color:#000\">a<\/span> <span style=\"color:#000\">-&gt;<\/span> <span style=\"color:#a90d91\">IO<\/span> <span style=\"color:#a90d91\">()<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">processStep<\/span> (<span style=\"color:#a90d91\">AuthStep<\/span> <span style=\"color:#000\">payload<\/span>) <span style=\"color:#000\">=<\/span> <span style=\"color:#000\">validateAuth<\/span> <span style=\"color:#000\">payload<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">processStep<\/span> (<span style=\"color:#a90d91\">ReviewStep<\/span> <span style=\"color:#000\">payload<\/span>) <span style=\"color:#000\">=<\/span> <span style=\"color:#000\">runReview<\/span> <span style=\"color:#000\">payload<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">-- Example Usage:<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">-- let authAction = AuthStep (AuthData &#34;user123&#34; &#34;secret_token&#34;)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">-- let reviewAction = ReviewStep (ReviewData 456 &#34;Looks good!&#34;)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">--<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">-- processStep authAction <\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">-- processStep reviewAction<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">--<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">-- The following would be a compile-time error:<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#177500\">-- processStep (AuthStep (ReviewData 789 &#34;Wrong data!&#34;)) -- Type mismatch!<\/span>\n<\/span><\/span><\/code><\/pre><\/div>"},{"title":"Composable Parsers with Attoparsec","link":"https:\/\/codehakase.com\/shorts\/composable-parsers-with-attoparsec\/","pubDate":"Tue, 06 May 2025 00:00:00 +0000","guid":"https:\/\/codehakase.com\/shorts\/composable-parsers-with-attoparsec\/","description":"<p>I&rsquo;ve been diving into Haskell&rsquo;s <a href=\"https:\/\/hackage.haskell.org\/package\/attoparsec\">attoparsec<\/a> and I&rsquo;m impressed by how it simplifies building complex parsers. Here&rsquo;s a quick example for a DSL snippet like <code>&quot;name=Some Name;age=42;&quot;<\/code>, using <code>Data.Attoparsec.Text<\/code>:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-haskell\" data-lang=\"haskell\"><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">import<\/span> <span style=\"color:#000\">Control.Applicative<\/span> ((<span style=\"color:#000\">&lt;*<\/span>), (<span style=\"color:#000\">*&gt;<\/span>))\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">import<\/span> <span style=\"color:#000\">Data.Attoparsec.Text<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">import<\/span> <span style=\"color:#000\">Data.Text<\/span> (<span style=\"color:#a90d91\">Text<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">import<\/span> <span style=\"color:#000\">GHC.Generics<\/span> (<span style=\"color:#a90d91\">Generic<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">data<\/span> <span style=\"color:#a90d91\">Person<\/span> <span style=\"color:#000\">=<\/span> <span style=\"color:#a90d91\">Person<\/span> { <span style=\"color:#000\">name<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">Text<\/span>, <span style=\"color:#000\">age<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">Int<\/span> }\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#a90d91\">deriving<\/span> (<span style=\"color:#a90d91\">Show<\/span>, <span style=\"color:#a90d91\">Eq<\/span>, <span style=\"color:#a90d91\">Generic<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">personParser<\/span> <span style=\"color:#000\">::<\/span> <span style=\"color:#a90d91\">Parser<\/span> <span style=\"color:#a90d91\">Person<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">personParser<\/span> <span style=\"color:#000\">=<\/span> <span style=\"color:#a90d91\">Person<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#000\">&lt;$&gt;<\/span> (<span style=\"color:#000\">string<\/span> <span style=\"color:#c41a16\">&#34;name=&#34;<\/span> <span style=\"color:#000\">*&gt;<\/span> <span style=\"color:#000\">takeTill<\/span> (<span style=\"color:#000\">==<\/span> <span style=\"color:#2300ce\">&#39;;&#39;<\/span>) <span style=\"color:#000\">&lt;*<\/span> <span style=\"color:#000\">char<\/span> <span style=\"color:#2300ce\">&#39;;&#39;<\/span>)\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#000\">&lt;*&gt;<\/span> (<span style=\"color:#000\">string<\/span> <span style=\"color:#c41a16\">&#34;age=&#34;<\/span> <span style=\"color:#000\">*&gt;<\/span> <span style=\"color:#000\">decimal<\/span> <span style=\"color:#000\">&lt;*<\/span> <span style=\"color:#000\">char<\/span> <span style=\"color:#2300ce\">&#39;;&#39;<\/span>)\n<\/span><\/span><\/code><\/pre><\/div><p>This parser defines a <code>Person<\/code> and then, using applicative style, consumes <code>&quot;name=&quot;<\/code>, takes characters until a semicolon (discarding the semicolon), then consumes <code>&quot;age=&quot;<\/code>, parses a decimal (discarding its trailing semicolon), and populates the <code>Person<\/code> fields.<\/p>"},{"title":"Implementing Non-Copyable Embedded Structs","link":"https:\/\/codehakase.com\/shorts\/implementing-non-copyable-embedded-structs\/","pubDate":"Tue, 06 May 2025 00:00:00 +0000","guid":"https:\/\/codehakase.com\/shorts\/implementing-non-copyable-embedded-structs\/","description":"<p>While working with embedded structs, I learned that even implicitly promoted fields can be silently copied if the parent struct is passed by value. This becomes dangerous when the embedded struct holds resources like mutexes, buffers, or channels. To guard against this, I now utilise a <code>noCopy<\/code> marker in the embedded struct itself, making any value-based use of the parent type inherently unsafe and detectable with vet or the race detector.<\/p>"},{"title":"Tmux sessionizer","link":"https:\/\/codehakase.com\/shorts\/tmux-sessionizer\/","pubDate":"Mon, 05 May 2025 00:00:00 +0000","guid":"https:\/\/codehakase.com\/shorts\/tmux-sessionizer\/","description":"<p>I came across <a href=\"https:\/\/github.com\/ThePrimeagen\/tmux-sessionizer\">tmux-sessionizer<\/a> today, and this has become handy in how I&rsquo;m managing tmux sesisons. For my workflow, I added an alias that&rsquo;ll launch <code>tmux-sessionizer<\/code>.\n<img src=\"https:\/\/blob.codehakase.com\/static\/images\/shorts\/tmux-sessionizer\/tmux-sessionizer.png\" alt=\"tmux-sessionizer\"><\/p>\n<p>I&rsquo;ve made some slight modifications to fit my dev setup.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"display:flex;\"><span><span style=\"color:#633820\">#!\/usr\/bin\/env bash\n<\/span><\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#633820\"><\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">if<\/span> <span style=\"color:#000\">[[<\/span> <span style=\"color:#000\">$#<\/span> -eq <span style=\"color:#1c01ce\">1<\/span> <span style=\"color:#000\">]]<\/span>; <span style=\"color:#a90d91\">then<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#000\">selected<\/span><span style=\"color:#000\">=<\/span><span style=\"color:#000\">$1<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">else<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#000\">selected<\/span><span style=\"color:#000\">=<\/span><span style=\"color:#a90d91\">$(<\/span>find ~\/Dev ~\/Dev\/personal ~\/Dev\/work ~\/Dev\/personal\/projects ~\/Dev\/personal\/opensource ~\/Dev\/personal\/learning -mindepth <span style=\"color:#1c01ce\">1<\/span> -maxdepth <span style=\"color:#1c01ce\">1<\/span> -type d | fzf<span style=\"color:#a90d91\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">fi<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">if<\/span> <span style=\"color:#000\">[[<\/span> -z <span style=\"color:#000\">$selected<\/span> <span style=\"color:#000\">]]<\/span>; <span style=\"color:#a90d91\">then<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#a90d91\">exit<\/span> <span style=\"color:#1c01ce\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">fi<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">selected_name<\/span><span style=\"color:#000\">=<\/span><span style=\"color:#a90d91\">$(<\/span>basename <span style=\"color:#c41a16\">&#34;<\/span><span style=\"color:#000\">$selected<\/span><span style=\"color:#c41a16\">&#34;<\/span> | tr . _<span style=\"color:#a90d91\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#000\">tmux_running<\/span><span style=\"color:#000\">=<\/span><span style=\"color:#a90d91\">$(<\/span>pgrep tmux<span style=\"color:#a90d91\">)<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">if<\/span> <span style=\"color:#000\">[[<\/span> -z <span style=\"color:#000\">$TMUX<\/span> <span style=\"color:#000\">]]<\/span> <span style=\"color:#000\">&amp;&amp;<\/span> <span style=\"color:#000\">[[<\/span> -z <span style=\"color:#000\">$tmux_running<\/span> <span style=\"color:#000\">]]<\/span>; <span style=\"color:#a90d91\">then<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> tmux new-session -s <span style=\"color:#000\">$selected_name<\/span> -c <span style=\"color:#000\">$selected<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> <span style=\"color:#a90d91\">exit<\/span> <span style=\"color:#1c01ce\">0<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">fi<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">if<\/span> ! tmux has-session -t<span style=\"color:#000\">=<\/span><span style=\"color:#000\">$selected_name<\/span> 2&gt; \/dev\/null; <span style=\"color:#a90d91\">then<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> tmux new-session -ds <span style=\"color:#000\">$selected_name<\/span> -c <span style=\"color:#000\">$selected<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">fi<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">if<\/span> <span style=\"color:#000\">[[<\/span> -n <span style=\"color:#000\">$TMUX<\/span> <span style=\"color:#000\">]]<\/span>; <span style=\"color:#a90d91\">then<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span> tmux switch-client -t <span style=\"color:#000\">$selected_name<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">else<\/span> \n<\/span><\/span><span style=\"display:flex;\"><span> tmux attach-session -t <span style=\"color:#000\">$selected_name<\/span>\n<\/span><\/span><span style=\"display:flex;\"><span><span style=\"color:#a90d91\">fi<\/span>\n<\/span><\/span><\/code><\/pre><\/div>"},{"title":"Scripting a self-updating Github Readme","link":"https:\/\/codehakase.com\/shorts\/scripting-a-self-updating-github-readme\/","pubDate":"Thu, 01 May 2025 00:00:00 +0000","guid":"https:\/\/codehakase.com\/shorts\/scripting-a-self-updating-github-readme\/","description":"<p>I recently updated the README <sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1<\/a><\/sup> on my <a href=\"https:\/\/github.com\/codehakase\/codehakase\">Github profile<\/a> to add a new blog post entry, which made me realise this manual process was a good candidate for automation.<\/p>\n<p><img src=\"https:\/\/blob.codehakase.com\/static\/images\/shorts\/self-updating-readme\/github-readme.png\" alt=\"Github profile for codehakase\"><\/p>\n<p>I took inspiration from <a href=\"https:\/\/simonwillison.net\/2020\/Jul\/10\/self-updating-profile-readme\/\">Simon Willison&rsquo;s blog<\/a> to utilise Github Actions for this. The <a href=\"https:\/\/github.com\/codehakase\/codehakase\/blob\/master\/.github\/workflows\/push.yml\">Github Action<\/a> calls a <a href=\"https:\/\/github.com\/codehakase\/codehakase\/tree\/master\/up\">script<\/a> that:<\/p>\n<ul>\n<li>Fetch and unmarshall rss feed items from this blog<\/li>\n<li>Render README template using feed data<\/li>\n<li>Updates the current <code>README.md<\/code> if there&rsquo;s a diff<\/li>\n<\/ul>\n<p>The core Go script orchestrates fetching the data and rendering the template. Here&rsquo;s a snippet:<\/p>"}]}}