<?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: Erick 🙃 Navarro</title>
    <description>The latest articles on DEV Community by Erick 🙃 Navarro (@erickgnavar).</description>
    <link>https://dev.to/erickgnavar</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%2F21645%2Fceabb8be-633c-4112-97ea-449072e944ad.jpg</url>
      <title>DEV Community: Erick 🙃 Navarro</title>
      <link>https://dev.to/erickgnavar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/erickgnavar"/>
    <language>en</language>
    <item>
      <title>Telegram bot to preview social media content</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Mon, 03 Feb 2025 23:27:15 +0000</pubDate>
      <link>https://dev.to/erickgnavar/telegram-bot-to-preview-social-media-content-1boi</link>
      <guid>https://dev.to/erickgnavar/telegram-bot-to-preview-social-media-content-1boi</guid>
      <description>&lt;p&gt;I usually share some links of Twitter(it is known as X now but I still call it Twitter) in Telegram, but for some reason the previews doesn't work well most of the times.&lt;/p&gt;

&lt;p&gt;One solution was to edit a Twitter link so I can use vxtwitter.com instead of twitter.com, this provides better previews but doing it that way is too manual so I made a bot to detect when I share a Twitter link and edit the message to use vxtwitter.com instead.&lt;/p&gt;

&lt;p&gt;The bot uses the library &lt;a href="https://pypi.org/project/Telethon/" rel="noopener noreferrer"&gt;telethon&lt;/a&gt;, it's a really easy to use python library. Also it has great documentation.&lt;/p&gt;

&lt;p&gt;This is the complete code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;telethon&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TelegramClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;

&lt;span class="c1"&gt;# get credentials from https://my.telegram.org, under API Development
# section, then fill the following section
&lt;/span&gt;&lt;span class="n"&gt;api_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;
&lt;span class="n"&gt;api_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# The first parameter is the .session file name (absolute paths allowed)
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TelegramClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# outgoing=True is important so we only listen to our own messages,
# otherwise this will listen to all the messages received, direct
# messages and group messages
&lt;/span&gt;&lt;span class="nd"&gt;@client.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NewMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outgoing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rewrite_twitter_links&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;reply_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vxtwitter.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# we must use the correct object to be able to edit the
&lt;/span&gt;        &lt;span class="c1"&gt;# message, otherwise we'll get an error
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_id&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;edit_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reply_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;link_preview&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_until_disconnected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is simple, it connect to Telegram API using a "personal bot", this means it will act on your behalf. It will detect when the message has a x.com link and it will replace it(making an edit) with vxtwitter.com so it will be have a nice preview :)&lt;/p&gt;

&lt;p&gt;This can be extended to do another nice things, for example: TikTok doesn't allow to see videos in mobile at least you have their app installed. In case someone send me a TikTok link I automatically respond with a preview message using vm.vxtiktok.com so I can see the video inside Telegram without having to install the app :).&lt;/p&gt;

&lt;p&gt;The code to do that is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@client.on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NewMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incoming&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rewrite_tiktok_links&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ignore group messages
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vm.tiktok.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tiktok.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vxtiktok.com&lt;/span&gt;&lt;span class="sh"&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 this case we respond only to a direct messages, that's why the early return when &lt;code&gt;event.is_group&lt;/code&gt; is true.&lt;/p&gt;

</description>
      <category>telegram</category>
      <category>bot</category>
    </item>
    <item>
      <title>Add D2 support to Hugo</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Thu, 13 Jun 2024 12:49:06 +0000</pubDate>
      <link>https://dev.to/erickgnavar/add-d2-support-to-hugo-15kl</link>
      <guid>https://dev.to/erickgnavar/add-d2-support-to-hugo-15kl</guid>
      <description>&lt;p&gt;&lt;a href="https://d2lang.com" rel="noopener noreferrer"&gt;D2&lt;/a&gt; is declarative language to generate diagrams, it's like mermaid on steroids, it has a &lt;code&gt;cli&lt;/code&gt; so it's easy to use.&lt;/p&gt;

&lt;p&gt;Hugo doesn't support it at the moment of writing this, there is an open &lt;a href="https://github.com/gohugoio/hugo/issues/10579" rel="noopener noreferrer"&gt;issue&lt;/a&gt; where the conversation is being done.&lt;/p&gt;

&lt;p&gt;So in the meantime official support is added we're going to make our own integration. It will have 2 parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A little HTTP server that receives the &lt;code&gt;d2&lt;/code&gt; code and return a resulting &lt;code&gt;SVG&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A custom &lt;a href="https://gohugo.io/render-hooks/code-blocks/" rel="noopener noreferrer"&gt;hugo code block render hook&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A simple HTTP server in &lt;code&gt;go&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This server only uses standard library and do the follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;receive &lt;code&gt;d2&lt;/code&gt; code in a &lt;code&gt;POST&lt;/code&gt; payload.&lt;/li&gt;
&lt;li&gt;call &lt;code&gt;d2&lt;/code&gt; cli and get its &lt;code&gt;std output&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;return this &lt;code&gt;output&lt;/code&gt; as response body.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bytes"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"os/exec"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handleRenderRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusBadRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;renderText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;renderText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// d2 -, will render the text received in stdin&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"d2"&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="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBuffer&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST /render"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handleRenderRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&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;blockquote&gt;
&lt;p&gt;We can also use &lt;code&gt;d2&lt;/code&gt; as a package but using it as a CLI is less code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now if we send a request the following request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST http://localhost:8080/render
shape: sequence_diagram
alice -&amp;gt; bob: What does it mean\nto be well-adjusted?
bob -&amp;gt; alice: The ability to play bridge or\ngolf as if they were games.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fb87gkae2lw8mlu91ygfy.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%2Fb87gkae2lw8mlu91ygfy.png" alt=" " width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom code block render hook
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Hugo&lt;/code&gt; allow us to define custom code block render hooks, we're going to define a custom one using &lt;code&gt;d2&lt;/code&gt; hook so when we define the following code it will call our server and insert its resulting &lt;code&gt;SVG&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;D2 code here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to create a new file in &lt;code&gt;layouts/_default/_markup/render-codeblock-d2.html&lt;/code&gt; and put the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{- $renderHookName := "d2" }}

{{- $inner := trim .Inner "\n\r" }}
{{- $position := .Position }}

{{- $apiEndpoint := "http://localhost:8080/render" }}

{{- $opts := dict "method" "post" "body" $inner }}
{{- with resources.GetRemote $apiEndpoint $opts }}
  {{- with .Err }}
    {{- errorf "The %q code block render hook was unable to get the remote diagram. See %s. %s" $renderHookName $position . }}
  {{- else }}
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width: 600px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {{ .Content | safeHTML }}
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  {{- end }}
{{- else }}
  {{- errorf "The %q code block render hook was unable to get the remote diagram. See %s" $renderHookName $position }}
{{- end }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code uses &lt;code&gt;resources.GetRemote&lt;/code&gt; to make a &lt;code&gt;POST&lt;/code&gt; request to our server and then insert the response content as part of the document.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The render will only be done on build time so we don't need to have the render server always on&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;&lt;code&gt;Hugo&lt;/code&gt; allows us to add custom features to our site and &lt;code&gt;go&lt;/code&gt; allows us to accomplish task using on only standard library.&lt;/p&gt;

&lt;p&gt;Enjoy! 🎉&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>d2</category>
    </item>
    <item>
      <title>Mermaid preview using xwidget browser</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Wed, 05 Jun 2024 14:19:05 +0000</pubDate>
      <link>https://dev.to/erickgnavar/mermaid-preview-using-xwidget-browser-fdg</link>
      <guid>https://dev.to/erickgnavar/mermaid-preview-using-xwidget-browser-fdg</guid>
      <description>&lt;p&gt;&lt;a href="https://mermaid.js.org" rel="noopener noreferrer"&gt;Mermaid.js&lt;/a&gt; is a great tool to make diagrams in plain text, I use it a lot and I wanted to have a way to see previews of the code I was writing.&lt;br&gt;
There are some options to do that but they require to have a &lt;a href="https://github.com/mermaid-js/mermaid-cli" rel="noopener noreferrer"&gt;mermaid-cli&lt;/a&gt; installed, which requires &lt;code&gt;nodejs&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;Emacs has a built-in webkit browser, in case it was compiled with &lt;code&gt;--with-xwidgets&lt;/code&gt; flag, and mermaid run on js so it should be possible to just run the code I want in the browser and see it there.&lt;/p&gt;

&lt;p&gt;This function makes the magic, it just take a region previously selected, which have the mermaid code, create a temp file and write some &lt;code&gt;HTML&lt;/code&gt; there(including our mermaid code).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/preview-mermaid&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Render region inside a webit embebed browser."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;region-active-p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;user-error&lt;/span&gt; &lt;span class="s"&gt;"Select a region first"&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="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;concat&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;make-temp-file&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;temporary-file-directory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="s"&gt;".html"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mermaid-code&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;buffer-substring-no-properties&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;region-beginning&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;region-end&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;save-excursion&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with-temp-buffer&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;insert&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;body&amp;gt;
  &amp;lt;pre class=\"mermaid\"&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;insert&lt;/span&gt; &lt;span class="nv"&gt;mermaid-code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;;; js script copied from mermaid documentation&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;insert&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;/pre&amp;gt;
  &amp;lt;script type=\"module\"&amp;gt;
    import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
    mermaid.initialize({ startOnLoad: true });
  &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;write-file&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;xwidget-webkit-browse-url&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt; &lt;span class="s"&gt;"file://%s"&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Demo
&lt;/h1&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%2Fu43wyn2xves6xj83ho42.gif" 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%2Fu43wyn2xves6xj83ho42.gif" alt="img" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>emacs</category>
      <category>mermaidjs</category>
    </item>
    <item>
      <title>Moving to Emacs Tree Sitter Modes</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Wed, 22 May 2024 04:30:15 +0000</pubDate>
      <link>https://dev.to/erickgnavar/moving-to-emacs-tree-sitter-modes-4jm</link>
      <guid>https://dev.to/erickgnavar/moving-to-emacs-tree-sitter-modes-4jm</guid>
      <description>&lt;p&gt;I'm currently using emacs 30 and this version has support for more languages using the new &lt;code&gt;ts-modes&lt;/code&gt;, I've been using &lt;a href="https://github.com/emacs-tree-sitter/elisp-tree-sitter/" rel="noopener noreferrer"&gt;emacs-tree-sitter&lt;/a&gt; package since Emacs added dynamic modules feature.&lt;/p&gt;

&lt;p&gt;Now using &lt;code&gt;emacs 30&lt;/code&gt; I give a try to use only &lt;code&gt;ts-modes&lt;/code&gt; and maybe delete some external packages I was using.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to install grammars
&lt;/h1&gt;

&lt;p&gt;Emacs doesn't have a built-in way to install automatically most common grammar so we're going to use a package that can handle that, we use &lt;a href="https://github.com/emacs-tree-sitter/tree-sitter-langs/" rel="noopener noreferrer"&gt;tree-sitter-langs&lt;/a&gt; package, which is a repository for grammars.&lt;/p&gt;

&lt;p&gt;When we install it it download and install all the available grammars in its own directory but to be used by emacs &lt;code&gt;ts-modes&lt;/code&gt; we need to put them inside &lt;code&gt;/.emacs.d/tree-sitter&lt;/code&gt; directory. I write a helper function to accomplish this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/copy-grammars-to-emacs-tree-sitter-dir&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Copy tree-sitter grammar files to native Emacs dir."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interactive&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="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;files&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;directory-files&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;tree-sitter-langs--bin-dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="s"&gt;"\\.dylib$"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dolist&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;grammar-file&lt;/span&gt; &lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;copy-file&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;concat&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;tree-sitter-langs--bin-dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;grammar-file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;concat&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;expand-file-name&lt;/span&gt; &lt;span class="nv"&gt;user-emacs-directory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"tree-sitter/"&lt;/span&gt; &lt;span class="s"&gt;"libtree-sitter-"&lt;/span&gt; &lt;span class="nv"&gt;grammar-file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt; &lt;span class="s"&gt;"%s grammar files copied"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt; &lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  The migration
&lt;/h1&gt;

&lt;p&gt;I use &lt;code&gt;straight.el&lt;/code&gt; to manage my packages so I'm going to use it to configure built-in &lt;code&gt;ts-modes&lt;/code&gt;, by now I only migrated a few modes.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;elixir&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;elixir-ts-mode&lt;/span&gt;
  &lt;span class="ss"&gt;:straight&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="nv"&gt;built-in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="ss"&gt;:mode&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"\\.ex\\'"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;elixir-ts-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\.exs\\'"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;elixir-ts-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\mix.lock\\'"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;elixir-ts-mode&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;docker&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;dockerfile-ts-mode&lt;/span&gt;
  &lt;span class="ss"&gt;:straight&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="nv"&gt;built-in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="ss"&gt;:defer&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
  &lt;span class="ss"&gt;:mode&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"\\Dockerfile\\'"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;dockerfile-ts-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\.dockerignore\\'"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;dockerfile-ts-mode&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;typescript&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;typescript-ts-mode&lt;/span&gt;
  &lt;span class="ss"&gt;:straight&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="nv"&gt;built-in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="ss"&gt;:defer&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
  &lt;span class="ss"&gt;:mode&lt;/span&gt; &lt;span class="s"&gt;"\\.tsx?\\'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;toml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;toml-ts-mode&lt;/span&gt;
  &lt;span class="ss"&gt;:straight&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="nv"&gt;built-in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="ss"&gt;:mode&lt;/span&gt; &lt;span class="s"&gt;"\\.toml\\'"&lt;/span&gt;
  &lt;span class="ss"&gt;:defer&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;yaml-ts-mode&lt;/span&gt;
  &lt;span class="ss"&gt;:straight&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="nv"&gt;built-in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="ss"&gt;:mode&lt;/span&gt; &lt;span class="s"&gt;"\\.ya?ml\\'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this now I have 5 dependencies less in my configuration :).&lt;/p&gt;

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

&lt;p&gt;I'm in the process to migrate others modes like &lt;code&gt;python-mode&lt;/code&gt;, &lt;code&gt;go-mode&lt;/code&gt; and so on but in some cases syntax highlighting is not the best. For example &lt;code&gt;shell-script-mode&lt;/code&gt; has better highlight than &lt;code&gt;bash-ts-mode&lt;/code&gt;. I'm been using these "new" &lt;code&gt;ts-modes&lt;/code&gt; for a few weeks and everything is working well.&lt;/p&gt;

&lt;p&gt;&lt;a id="org624b87d"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Bonus
&lt;/h1&gt;

&lt;p&gt;I also updated a &lt;a href="https://github.com/erickgnavar/tree-sitter-ispell.el/" rel="noopener noreferrer"&gt;package&lt;/a&gt; I use to run &lt;code&gt;ispell&lt;/code&gt; on text nodes to support built-in tree-sitter support. Now it supports &lt;code&gt;tree-sitter.el&lt;/code&gt; and &lt;code&gt;treesit&lt;/code&gt; packages.&lt;/p&gt;

</description>
      <category>emacs</category>
      <category>treesitter</category>
    </item>
    <item>
      <title>Simple OBS Client in Elixir</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Sun, 12 May 2024 19:49:50 +0000</pubDate>
      <link>https://dev.to/erickgnavar/simple-obs-client-in-elixir-3f86</link>
      <guid>https://dev.to/erickgnavar/simple-obs-client-in-elixir-3f86</guid>
      <description>&lt;p&gt;Let's write a simple module to control OBS using websockets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://obsproject.com" rel="noopener noreferrer"&gt;OBS&lt;/a&gt; already have a websocket server which can accept many command to control its features. We're going to use that websocket server to make some actions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Change scene&lt;/li&gt;
&lt;li&gt;  Apply source filters&lt;/li&gt;
&lt;li&gt;  Many other things&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Basic configuration
&lt;/h1&gt;

&lt;p&gt;First we need to install a websocket client, &lt;a href="https://hex.pm/packages/fresh" rel="noopener noreferrer"&gt;fresh&lt;/a&gt; is a easy to use client that works on top on &lt;a href="https://hex.pm/packages/mint" rel="noopener noreferrer"&gt;mint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's add the following code to our &lt;code&gt;mix.exs&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;     {:earmark, "~&amp;gt; 1.4"},
&lt;span class="gi"&gt;+    {:fresh, "~&amp;gt; 0.4.4"}
&lt;/span&gt;   ]
 end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to define a module that use all the functionality of &lt;code&gt;fresh&lt;/code&gt;, like the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;OBS&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Fresh&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"op"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"d"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;rpcVersion:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Jason&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point this just connect to the websocket and responds with a specific message &lt;code&gt;{"op": 1, "d": {"rpcVersion": 1}}&lt;/code&gt;, this is required to "authenticate" against OBS websocket server. This according to &lt;a href="https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#hello-opcode-0" rel="noopener noreferrer"&gt;OBS websocket protocol&lt;/a&gt;. In case we set up a password in our OBS settings we need to modify this response.&lt;/p&gt;

&lt;p&gt;Now we need to start our client, we can do it manually or using &lt;code&gt;application.ex&lt;/code&gt; like the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; @impl true
 def start(_type, _args) do
   children = [
&lt;span class="gi"&gt;+    # OBS websocket client
+    {MyApp.OBS,
+     uri: "ws://localhost:4455", state: nil, opts: [name: {:local, :obs_websocket_client}]}
&lt;/span&gt;   ]
 end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is where we define 2 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  the location of the websocket server, if we are running &lt;code&gt;OBS&lt;/code&gt; in the same machine we can use &lt;code&gt;ws://localhost:4455&lt;/code&gt;, this is the default address that &lt;code&gt;OBS&lt;/code&gt; uses.&lt;/li&gt;
&lt;li&gt;  the "name" of the process that will be used later to send messages to. In this case is &lt;code&gt;:obs_websocket_client&lt;/code&gt;. The client is just an &lt;code&gt;erlang&lt;/code&gt; process so we need some way to send messages to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Controlling OBS
&lt;/h1&gt;

&lt;p&gt;Now that we have a working websocket client we can send messages to &lt;code&gt;OBS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are going to implement a function to change the scene in &lt;code&gt;OBS&lt;/code&gt;, to do that we need to use the &lt;a href="https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#setcurrentprogramscene" rel="noopener noreferrer"&gt;SetCurrentProgramScene&lt;/a&gt; action.&lt;/p&gt;

&lt;p&gt;Let's add 3 functions to our &lt;code&gt;OBS&lt;/code&gt; module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+def change_scene(scene_name) do
+  send_message("SetCurrentProgramScene", %{"sceneName" =&amp;gt; scene_name})
+end
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+defp send_message(type, data) do
+  payload = %{
+    "op" =&amp;gt; 6,
+    "d" =&amp;gt; %{
+      # generate a random identifier, we can use any other module
+      "requestId" =&amp;gt; Ecto.UUID.generate(),
+      "requestType" =&amp;gt; type,
+      "requestData" =&amp;gt; data
+    }
+  }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+  send(:obs_websocket_client, {:send, Jason.encode!(payload)})
+end
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+def handle_info({:send, message}, state) do
+  {:reply, [{:text, message}], state}
+end
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;send_message/2&lt;/code&gt;, receive a type and a map, it will use these to prepare a payload which will be send to our websocket client. We're using &lt;code&gt;send/2&lt;/code&gt; because our client is just another process, which have the name defined previously in &lt;code&gt;application.ex&lt;/code&gt;: &lt;code&gt;:obs_websocket_client&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;handle_info/2&lt;/code&gt;, this will receive the message and send it to the websocket server, because &lt;code&gt;OBS&lt;/code&gt; protocol just use plain text we use a &lt;code&gt;{:text, message}&lt;/code&gt; response.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;change_scene/1&lt;/code&gt; this just reuse the previously defined functions. We send the &lt;code&gt;SetCurrentProgramScence&lt;/code&gt; message with a specific payload. Now we can just call &lt;code&gt;MyApp.OBS.change_scene("gaming")&lt;/code&gt; and it will be changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enjoy 🎉.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>websocket</category>
      <category>obs</category>
    </item>
    <item>
      <title>Switching from helm to vertico and friends</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Fri, 01 Sep 2023 05:23:06 +0000</pubDate>
      <link>https://dev.to/erickgnavar/switching-from-helm-to-vertico-and-friends-1pph</link>
      <guid>https://dev.to/erickgnavar/switching-from-helm-to-vertico-and-friends-1pph</guid>
      <description>&lt;p&gt;I've been using &lt;code&gt;helm&lt;/code&gt; for many years, since I started to use &lt;code&gt;emacs&lt;/code&gt; itself. It was great because it is a "all in one" tool, very convenient when starting in &lt;code&gt;emacs&lt;/code&gt; world.&lt;/p&gt;

&lt;h1&gt;
  
  
  The problem(?)
&lt;/h1&gt;

&lt;p&gt;Actually there is a not problem perse, just maybe the "startup time", I had the load deferred so the first time I call some function from &lt;code&gt;helm&lt;/code&gt; there was a little "wait", nothing really bad but a little annoying&lt;/p&gt;

&lt;h1&gt;
  
  
  Motivation
&lt;/h1&gt;

&lt;p&gt;Just wanted to trying new things 😅&lt;/p&gt;

&lt;h1&gt;
  
  
  The new stuff
&lt;/h1&gt;

&lt;p&gt;There are new tools that are gaining popularity because of their approach: "just do one thing", these tools are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://elpa.gnu.org/packages/vertico.html" rel="noopener noreferrer"&gt;vertico&lt;/a&gt;: UI for completion, based on minibuffer, it also has a option to use a separate buffer, just like &lt;code&gt;helm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/minad/marginalia" rel="noopener noreferrer"&gt;marginalia&lt;/a&gt;: Enhance the minibuffer with more context about the information shown&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/oantolin/orderless" rel="noopener noreferrer"&gt;orderless&lt;/a&gt;: completion style, add fuzzy search to filter between the data shown by &lt;code&gt;vertico&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/minad/consult" rel="noopener noreferrer"&gt;consult&lt;/a&gt;: commands for search and navigation&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/oantolin/embark" rel="noopener noreferrer"&gt;embark&lt;/a&gt;: runs commands given a context at point&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these tools rely on &lt;code&gt;emacs&lt;/code&gt; builtin functionality as much as possible and try to adhere to the APIs of different builtin components, that's a nice approach :)&lt;/p&gt;

&lt;p&gt;&lt;a id="org48958d2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The migration
&lt;/h1&gt;

&lt;p&gt;Before migrating I needed to confirm that there were a replacement for every(or at least more of them) feature I use in my workflow, these features are:&lt;/p&gt;

&lt;p&gt;&lt;a id="orgdffc7d5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fuzzy search in a project
&lt;/h2&gt;

&lt;p&gt;I was using &lt;a href="https://github.com/emacs-helm/helm-ls-git" rel="noopener noreferrer"&gt;helm-ls-git&lt;/a&gt;, which works on top oh &lt;code&gt;helm&lt;/code&gt; and also put at the top all the files that have been modified, it uses &lt;code&gt;git&lt;/code&gt; to get that information, it very useful because it give more context when searching for a file.&lt;/p&gt;

&lt;p&gt;I haven't found(yet) something that replace &lt;code&gt;helm-ls-git&lt;/code&gt; but for fuzzy search I just can call &lt;code&gt;project-find-file&lt;/code&gt;, which is builtin in &lt;code&gt;emacs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="org46e1f64"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Search and replace in a whole project
&lt;/h2&gt;

&lt;p&gt;For this I was using &lt;a href="https://github.com/emacsorphanage/helm-ag" rel="noopener noreferrer"&gt;helm-ag&lt;/a&gt;, it allow to search into project files content, make another filter using &lt;code&gt;helm&lt;/code&gt; and also create an editable buffer with the results, once all the edits are done we can modify all the matched files with a single command.&lt;/p&gt;

&lt;p&gt;This can be done using &lt;code&gt;embark-export&lt;/code&gt; function, which export all the content in the minibuffer and create a new buffer and use &lt;a href="https://github.com/mhayashi1120/Emacs-wgrep" rel="noopener noreferrer"&gt;wgrep&lt;/a&gt; to have a editable buffer, that's a lot of manual steps, in &lt;code&gt;helm-ag&lt;/code&gt; it was more easier so I wrote some code to have the same experience&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/grep-edit-results&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Export results using `embark-export' and activate `wgrep'.
This only runs for ripgrep results"&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;cl-search&lt;/span&gt; &lt;span class="s"&gt;"Ripgrep"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;buffer-string&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;;; we use `run-at-time' to ensure all of these steps&lt;/span&gt;
    &lt;span class="c1"&gt;;; will be executed in order&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;run-at-time&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="nf"&gt;#'&lt;/span&gt;&lt;span class="nv"&gt;embark-export&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;run-at-time&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="nf"&gt;#'&lt;/span&gt;&lt;span class="nv"&gt;wgrep-change-to-wgrep-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;run-at-time&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="nf"&gt;#'&lt;/span&gt;&lt;span class="nv"&gt;evil-normal-state&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;define-key&lt;/span&gt; &lt;span class="nv"&gt;minibuffer-mode-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;"C-c C-e"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;#'&lt;/span&gt;&lt;span class="nv"&gt;my/grep-edit-results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be executed from the &lt;code&gt;minibuffer&lt;/code&gt; and only when it is a &lt;code&gt;Ripgrep&lt;/code&gt; execution, this way I have the same as I used to with &lt;code&gt;helm-ag&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigate the kill ring
&lt;/h2&gt;

&lt;p&gt;Many times I delete stuff from many buffers and put them together in a new buffer, to do that I rely on the &lt;code&gt;kill-ring&lt;/code&gt; history, &lt;code&gt;helm&lt;/code&gt; has a function &lt;code&gt;helm-show-kill-ring&lt;/code&gt; with allow to fuzzy search in the history and then paste the selected item into the current buffer. &lt;code&gt;consult&lt;/code&gt; has an option to that too &lt;code&gt;consult-yank-from-kill-ring&lt;/code&gt;. It is not show the options as &lt;code&gt;helm&lt;/code&gt; but it does the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fuzzy search inside a buffer
&lt;/h2&gt;

&lt;p&gt;For this I used &lt;a href="https://github.com/emacsorphanage/helm-swoop" rel="noopener noreferrer"&gt;helm-swoop&lt;/a&gt;, &lt;code&gt;consult&lt;/code&gt; already has a function that do the same, it is &lt;code&gt;consult-line&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Backup files
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/antham/helm-backup" rel="noopener noreferrer"&gt;helm-backup&lt;/a&gt; is great, it create a backup of your current file on every save and store it in a git repository, it allow to look for all the versions using &lt;code&gt;helm&lt;/code&gt;, I wanted to still use it but I don't want to have &lt;code&gt;helm&lt;/code&gt; installed just for one package, fortunately the author has another package &lt;a href="https://github.com/antham/git-backup" rel="noopener noreferrer"&gt;git-backup&lt;/a&gt; which has all the functionality for backups has no dependency on &lt;code&gt;helm&lt;/code&gt;, with this I was able to reproduce what I have and with some &lt;code&gt;elisp&lt;/code&gt; code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;my/backup-dir&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;expand-file-name&lt;/span&gt; &lt;span class="s"&gt;"~/.git-backup"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/git-backup-versioning&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Save a version of the current file."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;featurep&lt;/span&gt; &lt;span class="ss"&gt;'git-backup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;'git-backup&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;git-backup-version-file&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;executable-find&lt;/span&gt; &lt;span class="s"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;my/backup-dir&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="nv"&gt;buffer-file-name&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/git-backup-run-action&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt; &lt;span class="nv"&gt;commit-hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="s"&gt;"Execute COMMAND with COMMIT-HASH using another defaults arguments."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;apply&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt; &lt;span class="o"&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;span class="nv"&gt;executable-find&lt;/span&gt; &lt;span class="s"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;my/backup-dir&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;commit-hash&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;buffer-file-name&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/git-backup&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Navigate in versions of the current file."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;featurep&lt;/span&gt; &lt;span class="ss"&gt;'git-backup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;'git-backup&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="c1"&gt;;; for some reason an extra space after `%h|' is required to avoid an error when&lt;/span&gt;
  &lt;span class="c1"&gt;;; the shell command is executed&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;candidates&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;git-backup-list-file-change-time&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;executable-find&lt;/span&gt; &lt;span class="s"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;my/backup-dir&lt;/span&gt; &lt;span class="s"&gt;"%cI|%h| %ar"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;buffer-file-name&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;selection&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;completing-read&lt;/span&gt; &lt;span class="s"&gt;"Pick revision: "&lt;/span&gt; &lt;span class="nv"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;commit-hash&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;nth&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string-split&lt;/span&gt; &lt;span class="nv"&gt;selection&lt;/span&gt; &lt;span class="s"&gt;"|"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;completing-read&lt;/span&gt; &lt;span class="s"&gt;"Choose action: "&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"diff"&lt;/span&gt; &lt;span class="s"&gt;"new buffer"&lt;/span&gt; &lt;span class="s"&gt;"replace current buffer"&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cond&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;string-equal&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt; &lt;span class="s"&gt;"diff"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;my/git-backup-run-action&lt;/span&gt; &lt;span class="ss"&gt;'git-backup-create-ediff&lt;/span&gt; &lt;span class="nv"&gt;commit-hash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;string-equal&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt; &lt;span class="s"&gt;"new buffer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;my/git-backup-run-action&lt;/span&gt; &lt;span class="ss"&gt;'git-backup-open-in-new-buffer&lt;/span&gt; &lt;span class="nv"&gt;commit-hash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;string-equal&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt; &lt;span class="s"&gt;"replace current buffer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;my/git-backup-run-action&lt;/span&gt; &lt;span class="ss"&gt;'git-backup-replace-current-buffer&lt;/span&gt; &lt;span class="nv"&gt;commit-hash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;t&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt; &lt;span class="s"&gt;"Not valid option"&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;git-backup&lt;/span&gt;
  &lt;span class="ss"&gt;:ensure&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
  &lt;span class="ss"&gt;:hook&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;after-save&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;my/git-backup-versioning&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Final thoughts
&lt;/h1&gt;

&lt;p&gt;For the time this post was written I've been using the new setup for just a few days, so far the experience was good, it feels a simple setup now and gave me some ideas for another features that I'd like to develop.&lt;/p&gt;

&lt;p&gt;Also you can see the diff after the migration in my &lt;a href="https://github.com/erickgnavar/dotfiles/commit/6d60f30385afff9eef2a1a7e81215ea53d9f84f4" rel="noopener noreferrer"&gt;dotfiles&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Special thanks to &lt;a href="https://github.com/oantolin" rel="noopener noreferrer"&gt;@oantolin&lt;/a&gt;, creator or &lt;code&gt;embark&lt;/code&gt;, for helping me with some doubts in the telegram channel of emacs in Spanish &lt;a href="https://t.me/emacs_es" rel="noopener noreferrer"&gt;emacs-es&lt;/a&gt;&lt;/p&gt;

</description>
      <category>emacs</category>
    </item>
    <item>
      <title>Run ispell on text nodes using tree sitter</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Sun, 26 Jun 2022 19:28:34 +0000</pubDate>
      <link>https://dev.to/erickgnavar/run-ispell-on-text-nodes-using-tree-sitter-4ocn</link>
      <guid>https://dev.to/erickgnavar/run-ispell-on-text-nodes-using-tree-sitter-4ocn</guid>
      <description>&lt;p&gt;&lt;a href="https://tree-sitter.github.io/tree-sitter/" rel="noopener noreferrer"&gt;tree-sitter&lt;/a&gt; is a great tool to have an incremental syntax tree of our code, in emacs it can be used to add syntax highlighting instead of using the regular regex based highlighting system. There are other use cases for this and now we're going to use it to build a simple tool to run &lt;a href="https://www.gnu.org/software/ispell/" rel="noopener noreferrer"&gt;ispell&lt;/a&gt; using the content of a text node, of course we can select the text manually but it will be easier and fancy to do it in a programmatic way using the syntax tree generated by &lt;code&gt;tree-sitter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Emacs doesn't have support by default for &lt;code&gt;tree-sitter&lt;/code&gt; so we need to install it, the following code will do it using &lt;code&gt;use-package&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;tree-sitter&lt;/span&gt;
  &lt;span class="ss"&gt;:ensure&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
  &lt;span class="ss"&gt;:hook&lt;/span&gt;
  &lt;span class="c1"&gt;;; enable highlight using tree-sitter instead of regex based system&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;tree-sitter-after-on&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;tree-sitter-hl-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="ss"&gt;:config&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;global-tree-sitter-mode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;tree-sitter-langs&lt;/span&gt;
  &lt;span class="ss"&gt;:ensure&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To build this tool we need two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Find a way to check if given our current position we are in a &lt;code&gt;string node&lt;/code&gt;, it can be a literal string, a multiple line string, a comment, etc.&lt;/li&gt;
&lt;li&gt;  Call &lt;code&gt;ispell&lt;/code&gt; pragmatically using the position of a &lt;code&gt;tree-sitter&lt;/code&gt; node.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Using tree-sitter to get node at the current position
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/emacs-tree-sitter/elisp-tree-sitter" rel="noopener noreferrer"&gt;tree-sitter&lt;/a&gt; package has some functions we can use, &lt;code&gt;tree-sitter-node-at-pos&lt;/code&gt; will give us the nearest node of a given type for our current position, so if we run &lt;code&gt;(tree-sitter-node-at-post 'string (point))&lt;/code&gt; if will return, in case it exists, a string node otherwise it will return &lt;code&gt;nil&lt;/code&gt;, using this we can check for any possible "string" values, in a programming language we can have string, comment and other elements which have text that need a spell check.&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;tree-sitter&lt;/code&gt; use a specific grammar for every programming language, a "string" element can have different names, for example in &lt;code&gt;python&lt;/code&gt; we have &lt;code&gt;string&lt;/code&gt; but in &lt;code&gt;go&lt;/code&gt; we have &lt;code&gt;interpreted_string_literal&lt;/code&gt;, we can check this values by running &lt;code&gt;M-x tree-sitter-debug-mode&lt;/code&gt; from a buffer using the language we want to know their "text" element names.&lt;/p&gt;

&lt;p&gt;Now we have to define a list with all the supported languages we want to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;tree-sitter-text-grammar-mapping&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;python-mode&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;go-mode&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interpreted_string_literal&lt;/span&gt; &lt;span class="nv"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;js-mode&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;template_string&lt;/span&gt; &lt;span class="nv"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;elixir-mode&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;comment&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 only support 4 languages, but it easy to add more, these are the ones I use more often.&lt;/p&gt;

&lt;p&gt;Now with the following function we can use the previous defined list of languages and extract a valid text node at current position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;get-text-node-at-point&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Get valid node for the current major mode using `tree-sitter-text-grammar-mapping'"&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;types&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;alist-get&lt;/span&gt; &lt;span class="nv"&gt;major-mode&lt;/span&gt; &lt;span class="nv"&gt;tree-sitter-text-grammar-mapping&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="c1"&gt;;; get string nodes from all the available nodes at the current point&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;matches&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;seq-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;tree-sitter-node-at-pos&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;types&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;filtered-matches&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;remove-if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;eq&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="c1"&gt;;; get first valid match&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;filtered-matches&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;car&lt;/span&gt; &lt;span class="nv"&gt;filtered-matches&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Call ispell using a tree-sitter text node
&lt;/h1&gt;

&lt;p&gt;We can use &lt;code&gt;ispell-region&lt;/code&gt; to run &lt;code&gt;ispell&lt;/code&gt; over a specific region, this function receives the start and end positions of a region so we need to extract those values from our &lt;code&gt;tree-sitter&lt;/code&gt; text node.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tsc-node-start-position&lt;/code&gt; and &lt;code&gt;tsc-node-end-position&lt;/code&gt; can be used for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;run-ispell-on-node&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="s"&gt;"Run ispell over the text of the received `node'"&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ispell-region&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;tsc-node-start-position&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;tsc-node-end-position&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Putting all together
&lt;/h1&gt;

&lt;p&gt;Now we can combine these two functions and assign it to a keybinding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;run-ispell-at-point&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Run ispell at current point if there is a text node."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interactive&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="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;node&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;get-text-node-at-point&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;run-ispell-on-node&lt;/span&gt; &lt;span class="nv"&gt;node&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;global-set-key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;"C-x C-s"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ss"&gt;'run-ispell-at-point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we run &lt;code&gt;C-x C-s&lt;/code&gt;, if we are at a text node, &lt;code&gt;ispell&lt;/code&gt; will run and check the spelling of that node.&lt;/p&gt;

&lt;p&gt;Enjoy.&lt;/p&gt;

</description>
      <category>emacs</category>
      <category>treesitter</category>
    </item>
    <item>
      <title>Auto build and publish emacs org configuration as a website</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Sun, 04 Apr 2021 16:32:08 +0000</pubDate>
      <link>https://dev.to/erickgnavar/auto-build-and-publish-emacs-org-configuration-as-a-website-2cl9</link>
      <guid>https://dev.to/erickgnavar/auto-build-and-publish-emacs-org-configuration-as-a-website-2cl9</guid>
      <description>&lt;p&gt;Having our emacs configuration in an &lt;code&gt;org&lt;/code&gt; file is great, it allow us to have it more organized and easy to read, but &lt;code&gt;org&lt;/code&gt; files have more features and one of them is the ability to be exported to different formats like HTML, PDF, markdown and so on. So what if we export our emacs configuration to HTML and then publish it in a website? 🤯&lt;/p&gt;

&lt;p&gt;It probably doesn't have any real utility but it would be nice to have a exclusive web page to show our emacs config to our friends :)&lt;/p&gt;

&lt;p&gt;We can do this in two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually, we can export the org file using the regular exportation feature of &lt;code&gt;org-mode&lt;/code&gt; and then upload the resulting HTML somewhere&lt;/li&gt;
&lt;li&gt;Automatically, our configuration will be rendered and published into a website every time we push some changes to our &lt;code&gt;dotfiles&lt;/code&gt; repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's define what we need to do to have the automatic way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have a script that render our org config file&lt;/li&gt;
&lt;li&gt;Run this script in a CI so it can be run every time we push some changes&lt;/li&gt;
&lt;li&gt;Push the rendered HTML to an extra repository&lt;/li&gt;
&lt;li&gt;Activate Github Pages in the extra repository, so we can have an url where we can see the resulting website&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's assume we have this structure in our &lt;code&gt;dotfiles&lt;/code&gt; repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;├── emacs
│   ├── config.org
│   └── init.el
└── scripts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a "regular" structure for a &lt;code&gt;dotfiles&lt;/code&gt; repository, the extra &lt;code&gt;scripts&lt;/code&gt; folder will be used later. Now let's deep into how it will work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Automate the org file rendering
&lt;/h1&gt;

&lt;p&gt;We need to create to files and put them inside &lt;code&gt;scripts&lt;/code&gt; folder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;render-emacs-config-to-html.sh&lt;/code&gt;, this will render our config file and place the resulting HTML file inside &lt;code&gt;scripts/output/index.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;org-render-html-minimal.el&lt;/code&gt;, this is a minimal config file to be able to render org into html, it load the required packages and make some basic configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's explore first &lt;code&gt;scripts/org-render-html-minimal.el&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;'package&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;package-archives&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"gnu"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"https://elpa.gnu.org/packages/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"melpa"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"https://melpa.org/packages/"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;package-initialize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;package-refresh-contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;;; this is required to highlight code blocks properly&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;package-install&lt;/span&gt; &lt;span class="ss"&gt;'htmlize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;'org&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;'htmlize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;;; For some reason the default value `inline-css' doesn't apply syntax highlighting correctly&lt;/span&gt;
&lt;span class="c1"&gt;;; in the resulting html file so we need to change the value to `css'&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;org-html-htmlize-output-type&lt;/span&gt; &lt;span class="ss"&gt;'css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To export code blocks correctly we need the package &lt;code&gt;htmlize&lt;/code&gt;, this package is available in MELPA so we need to configure MELPA and then install it from there.&lt;/p&gt;

&lt;p&gt;Now let's check &lt;code&gt;scripts/render-emacs-config-to-html.sh&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;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="c"&gt;# read the docs based theme&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"#+SETUPFILE: https://raw.githubusercontent.com/fniessen/org-html-themes/master/org/theme-readtheorg.setup"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; index.org
&lt;span class="nb"&gt;cat&lt;/span&gt; ../emacs/config.org &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; index.org

emacs index.org &lt;span class="nt"&gt;--batch&lt;/span&gt; &lt;span class="nt"&gt;-Q&lt;/span&gt; &lt;span class="nt"&gt;--load&lt;/span&gt; org-render-html-minimal.el &lt;span class="nt"&gt;-f&lt;/span&gt; org-html-export-to-html &lt;span class="nt"&gt;--kill&lt;/span&gt;

&lt;span class="c"&gt;# output will be the directory uploaded to the render repository so we have to put all the resulting files inside that folder&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;output
&lt;span class="nb"&gt;mv &lt;/span&gt;index.html output/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What are we doing here?&lt;/p&gt;

&lt;p&gt;We basically create a new org file called &lt;code&gt;index.org&lt;/code&gt; and put a setup configuration file in it. You can avoid this step if you put this line directly in your config file, in this case we're using one of the themes available in this &lt;a href="https://github.com/fniessen/org-html-themes" rel="noopener noreferrer"&gt;repository&lt;/a&gt;, there is more themes available in this other &lt;a href="https://olmon.gitlab.io/org-themes/" rel="noopener noreferrer"&gt;repository&lt;/a&gt; so you can choose the one you like the most.&lt;/p&gt;

&lt;p&gt;Now we need to run emacs with our previously defined configuration &lt;code&gt;org-render-html-minimal.el&lt;/code&gt; and tell it to render our &lt;code&gt;index.org&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And finally we put the resulting &lt;code&gt;index.html&lt;/code&gt; inside &lt;code&gt;output&lt;/code&gt; folder. This folder will be used later.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using Github actions to build and publish the rendered config
&lt;/h1&gt;

&lt;p&gt;We're going to use a Github action called &lt;a href="https://github.com/marketplace/actions/push-directory-to-another-repository" rel="noopener noreferrer"&gt;push-directory-to-another-repository&lt;/a&gt;, this action allow us to commit and push changes in another repository. Some configuration is required to use this action:&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a extra repository
&lt;/h2&gt;

&lt;p&gt;This extra repository will be used to host our rendered config file, in my case this repository is &lt;code&gt;erickgnavar/emacs-config&lt;/code&gt;, we also need to activate Github Pages in this repository and set it up to use &lt;code&gt;master&lt;/code&gt; branch&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%2Fonbq93cid83eq2sim4sj.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%2Fonbq93cid83eq2sim4sj.png" alt="set-up-github-pages" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The url generated, &lt;a href="https://erickgnavar.github.io/emacs-config/" rel="noopener noreferrer"&gt;erickgnavar.github.io/emacs-config&lt;/a&gt; in my case, is where our rendered config file will be published.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a personal token
&lt;/h2&gt;

&lt;p&gt;To be able to push changes into the new repository we have to create a personal access token, this can be made in &lt;a href="https://github.com/settings/tokens/" rel="noopener noreferrer"&gt;account settings&lt;/a&gt;, this token should have the &lt;code&gt;repo&lt;/code&gt; scoped activated.&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%2Flk9wqqe0l9nfh1h51229.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%2Flk9wqqe0l9nfh1h51229.png" alt="generate-github-api-token" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure a secret variable in our dotfiles repository
&lt;/h2&gt;

&lt;p&gt;The Github action needs a secret variable called &lt;code&gt;API_TOKEN_GITHUB&lt;/code&gt;, this variable allow the action to push changes into the new repository, we can create it by going to &lt;code&gt;repository/settings/secrets/New repository secret&lt;/code&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%2Fl1cqcd7hkirbc6g5h0be.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%2Fl1cqcd7hkirbc6g5h0be.png" alt="create-api-token-secret" width="800" height="531"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Github action config file
&lt;/h2&gt;

&lt;p&gt;Finally we have to create a file &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; with the following content:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-emacs-config-page&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;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpine:3.13.4&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@v2&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;Install emacs&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;apk --update add emacs&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;Render config into html&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;cd scripts &amp;amp;&amp;amp; sh render-emacs-config-to-html.sh&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;Pushes to destination repository&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;cpina/github-action-push-to-another-repository@cp_instead_of_deleting&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;API_TOKEN_GITHUB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.API_TOKEN_GITHUB }}&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;source-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scripts/output'&lt;/span&gt;
          &lt;span class="na"&gt;destination-github-username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;YOUR_GITHUB_USERNAME'&lt;/span&gt;
          &lt;span class="na"&gt;destination-repository-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;YOUR_NEW_REPOSITORY_NAME'&lt;/span&gt;
          &lt;span class="na"&gt;user-email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bot@emacs.bot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This action config file make some things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install emacs so we can run it to render our config file&lt;/li&gt;
&lt;li&gt;Render our config file using the script &lt;code&gt;render-emacs-config-to-html.sh&lt;/code&gt; we previously defined&lt;/li&gt;
&lt;li&gt;Take the content of &lt;code&gt;scripts/output&lt;/code&gt;, commit and push it into our destination repository, this is why we need to move the resulting HTML file into &lt;code&gt;output&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;And finally it calls &lt;code&gt;github-action-push-to-another-repository&lt;/code&gt; action which will do all the &lt;code&gt;git&lt;/code&gt; stuff required to push the changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now every time we push changes to our &lt;code&gt;dotfiles&lt;/code&gt; repository this action will push the rendered config file to our destination repository, the commits will look like this:&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%2Fd8hu6h2xvlpyojqkg87j.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%2Fd8hu6h2xvlpyojqkg87j.png" alt="destination-repo-commits-list" width="800" height="311"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And when we enter to the url generated from Github pages, &lt;a href="https://erickgnavar.github.io/emacs-config/" rel="noopener noreferrer"&gt;erickgnavar.github.io/emacs-config&lt;/a&gt; in my case, we can see our configuration rendered:&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%2Fqm0ll73dpd7x72ds2r2u.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%2Fqm0ll73dpd7x72ds2r2u.png" alt="rendered-config-result-page" width="800" height="484"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Enjoy 🎉&lt;/p&gt;

</description>
      <category>emacs</category>
      <category>orgmode</category>
    </item>
    <item>
      <title>Easy deploy of docker based projects</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Sat, 03 Apr 2021 03:57:24 +0000</pubDate>
      <link>https://dev.to/erickgnavar/easy-deploy-of-docker-based-projects-59pn</link>
      <guid>https://dev.to/erickgnavar/easy-deploy-of-docker-based-projects-59pn</guid>
      <description>&lt;p&gt;I have a personal server where I run some projects, some of them written in python, elixir and other technologies so having to deal with specific installation of any of these technologies is not an ideal workflow, to fix this I use docker and all of them are deployed using &lt;code&gt;docker-compose&lt;/code&gt;, they're connected to a single PostgreSQL server and they're behind the same web server.&lt;/p&gt;

&lt;p&gt;Running all of these projects in this way it's easier to maintain and in case something happens with the server I can re deploy everything in a easy way. Let's take a look to these tools and how they work together.&lt;/p&gt;

&lt;p&gt;Let's assume we have the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy a &lt;a href="https://github.com/erickgnavar/demo-projects/tree/master/simple-django-project-with-docker" rel="noopener noreferrer"&gt;django application&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deploy a &lt;a href="https://github.com/erickgnavar/demo-projects/tree/master/simple-phoenix-project-with-docker" rel="noopener noreferrer"&gt;phoenix application&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Each application needs a PostgreSQL database&lt;/li&gt;
&lt;li&gt;Both applications should be behind a web server and being accessed over HTTPS&lt;/li&gt;
&lt;li&gt;All of these should run in the same server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve this we're going to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a linux server&lt;/li&gt;
&lt;li&gt;Install PostgreSQL&lt;/li&gt;
&lt;li&gt;Configure a web server which will handle incoming traffic and SSL termination&lt;/li&gt;
&lt;li&gt;Run our applications inside Docker containers&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Setting up a server
&lt;/h1&gt;

&lt;p&gt;If you already have a server you can skip this section.&lt;/p&gt;

&lt;p&gt;We first need a server which can run docker, most linux distros can be used for this but in this case we'll be using Ubuntu Server, if you don't have a server yet you can use any of these referral links to get some credit when you create your account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://m.do.co/c/330e2b815378" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt; this will get you $100 on credits to be used in 2 months&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hetzner.cloud/?ref=om6r8Z9OZrQq" rel="noopener noreferrer"&gt;Hetzner&lt;/a&gt; this will get you 20€ on credits, this provider has cheaper prices than Digital Ocean but it only have data centers in Europe&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.linode.com/lp/brand-free-credit-short/" rel="noopener noreferrer"&gt;Linode&lt;/a&gt; This is not a referral link but you can get $100 on credits, AFAIK it doesn't expire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you get a server it's recommended to make some basic configurations like updating packages, setup a firewall, etc. You can follow this Linode &lt;a href="https://www.linode.com/docs/security/securing-your-server/" rel="noopener noreferrer"&gt;guide&lt;/a&gt; to secure your server.&lt;/p&gt;

&lt;p&gt;After that you need to install docker, to do that you can follow the &lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;, this have specific instructions for you linux distribution.&lt;/p&gt;

&lt;p&gt;Once we have &lt;code&gt;docker&lt;/code&gt; and &lt;code&gt;docker-compose&lt;/code&gt; installed we can follow this guide.&lt;/p&gt;

&lt;h1&gt;
  
  
  Installing PostgreSQL in our host machine
&lt;/h1&gt;

&lt;p&gt;We're going to use a unique instance of PostgreSQL installed in the host machine, this way we can share the resources used by PostgreSQL with all the applications that we're going to deploy, we just need to create new users and databases for each one of the applications.&lt;/p&gt;

&lt;p&gt;First let's install PostgreSQL with:&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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;postgresql-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to login with &lt;code&gt;postgres&lt;/code&gt; user so we can be able to enter to a &lt;code&gt;psql&lt;/code&gt; session. We can do it with:&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;sudo &lt;/span&gt;su - postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can open a &lt;code&gt;psql&lt;/code&gt; session and create the databases and users for our applications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;ENCRYPTED&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'secret'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;OWNER&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we do the same for our phoenix application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;phoenix&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;ENCRYPTED&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'secret'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;phoenix&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;OWNER&lt;/span&gt; &lt;span class="n"&gt;phoenix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Configuring Caddy as a reverse proxy
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://caddyserver.com" rel="noopener noreferrer"&gt;Caddy&lt;/a&gt; is a "new" web server written in Go that have 2 main features that make it a good option for simpler deployments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simpler configuration file&lt;/li&gt;
&lt;li&gt;Free auto configured SSL certificates, using Let's Encrypt service, and automatic renewals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we were using for example Nginx we have to deal with HTTPS certificates by ourselves, installing &lt;a href="https://certbot.eff.org" rel="noopener noreferrer"&gt;certbot&lt;/a&gt;, and also have to configure some way to renew the certificates, Let's Encrypt issues certificates that expire after 3 months.&lt;/p&gt;

&lt;p&gt;Let's define our domains &lt;code&gt;django.domain.com&lt;/code&gt; and &lt;code&gt;phoenix.domain.com&lt;/code&gt; which will send traffic to their specific applications.&lt;/p&gt;

&lt;p&gt;Our django application needs that Caddy serves the static files so we define &lt;code&gt;file_server&lt;/code&gt; option and tell caddy where are our static files, we also tell Caddy to send the traffic to port &lt;code&gt;8000&lt;/code&gt; where our application is listening.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django.domain.com {
    root * /opt/django

    @notStatic {
        not path /static/*
    }

    reverse_proxy @notStatic localhost:8000
    file_server
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our phoenix application will serve static files by itself so we just need to define the &lt;code&gt;reverse_proxy&lt;/code&gt; directive to be able to send the traffic to port &lt;code&gt;4000&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;phoenix.domain.com {
    reverse_proxy localhost:4000
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we reload our caddy server with &lt;code&gt;sudo systemctl reload caddy&lt;/code&gt; it will get the SSL certificates and internally will check if they still valid, otherwise it will renew them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Running our projects with docker-compose
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker compose&lt;/a&gt; is a tool that allow us to define different docker services in a easier way using a &lt;code&gt;yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;We're going to configure our two projects using &lt;code&gt;docker-compose&lt;/code&gt; but we first need their docker images so let's build them.&lt;/p&gt;

&lt;p&gt;Let's clone our projects(both are in the same repository, just in different folders), build the images and then publish them on a registry.&lt;/p&gt;

&lt;p&gt;This can be made in a separate machine because once the images are pushed to a remote registry they can be downloaded in our server.&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;cd &lt;/span&gt;simple-django-project-with-docker
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; registry.mycompany.com/django:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
docker push registry.mycompany.com/django:v1

&lt;span class="nb"&gt;cd &lt;/span&gt;simple-phoenix-project-with-docker
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; registry.mycompany.com/phoenix:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
docker push registry.mycompany.com/phoenix:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use docker hub to push your images or use Gitlab registry in case you want free private images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Django application
&lt;/h2&gt;

&lt;p&gt;Let's create a folder in &lt;code&gt;/opt/django&lt;/code&gt; and put the following code into a &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.mycompany.com/django:v1&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;network_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ALLOWED_HOSTS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.domain.com"&lt;/span&gt;
      &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0"&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://django:secret@localhost:5432/django"&lt;/span&gt;
      &lt;span class="na"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config.settings"&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;32&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;long&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secret&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;key"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./static:/app/static&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:8000:8000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;static&lt;/code&gt; folder will be used by Caddy to server static files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phoenix application
&lt;/h2&gt;

&lt;p&gt;Now for our phoenix application let's create a folder &lt;code&gt;/opt/phoenix&lt;/code&gt; and put the following code into a &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.mycompany.com/phoenix:v1&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;network_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://phoenix:secret@localhost:5432/phoenix"&lt;/span&gt;
      &lt;span class="na"&gt;MIX_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
      &lt;span class="na"&gt;HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phoenix.domain.com"&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;32&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;long&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;secret&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;key"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:4000:4000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we're running PostgreSQL in our host machine instead of a docker container we have to use &lt;code&gt;network_mode: host&lt;/code&gt;, this allow us to access postgres just pointing to &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying our projects
&lt;/h2&gt;

&lt;p&gt;Once we have the &lt;code&gt;docker-compose.yml&lt;/code&gt; files configured we can go inside each project folder and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the django application we also have to run these commands, these are specific of django deployment process.&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;# Run database migrations&lt;/span&gt;
docker-compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; web python manage.py migrate

&lt;span class="c"&gt;# Collect all static files and place them in our STATIC_ROOT folder which will be served by Caddy&lt;/span&gt;
docker-compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; web python manage.py collectstatic &lt;span class="nt"&gt;--no-input&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Deploying new changes
&lt;/h1&gt;

&lt;p&gt;Because we're using docker, when we need to update changes we just need to update their Docker images and restart their services. Some technologies can have differences in their deployment process but the basic idea is the same.&lt;/p&gt;

&lt;p&gt;Let's see how it could be for our two example applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Django application
&lt;/h2&gt;

&lt;p&gt;When we update a django application we need to run some extra commands like &lt;code&gt;migrate&lt;/code&gt;, &lt;code&gt;collectstatic&lt;/code&gt;, etc. We can follow these steps to run them inside the docker container:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"s/image.*/image:&lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="s2"&gt;NEW_DJANGO_IMAGE/"&lt;/span&gt; docker-compose.yml

docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--force-recreate&lt;/span&gt;

docker-compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; web python manage.py migrate

docker-compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; web python manage.py collectstatic &lt;span class="nt"&gt;--no-input&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're pulling the new image from our registry, updating the image value in our &lt;code&gt;docker-compose.yml&lt;/code&gt; file, restart the service (it will use the new image now) and then we can execute &lt;code&gt;migrate&lt;/code&gt; and &lt;code&gt;collectstatic&lt;/code&gt; commands&lt;/p&gt;

&lt;h2&gt;
  
  
  Phoenix application
&lt;/h2&gt;

&lt;p&gt;For the phoenix application we're going to follow almost the same process with just one difference, we don't need to run migrations in a separate step because they will run when the application starts, this is defined in the phoenix docker image itself.&lt;/p&gt;

&lt;p&gt;So we just need to pull the new image, update it in &lt;code&gt;docker-compose.yml&lt;/code&gt; file and then restart the service, the final script will be:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"s/image.*/image:&lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="s2"&gt;NEW_PHOENIX_IMAGE/"&lt;/span&gt; docker-compose.yml

docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--force-recreate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Having a central PostgreSQL instance and a central web server(Caddy), both in the host machine instead of inside a container allow us to manage them easily and also allow us to share these common services alongside the many applications that we are running in our server.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>caddy</category>
      <category>deployment</category>
      <category>linux</category>
    </item>
    <item>
      <title>Using compilation mode to run all the things</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Thu, 01 Apr 2021 16:44:22 +0000</pubDate>
      <link>https://dev.to/erickgnavar/using-compilation-mode-to-run-all-the-things-231o</link>
      <guid>https://dev.to/erickgnavar/using-compilation-mode-to-run-all-the-things-231o</guid>
      <description>&lt;p&gt;Compilation mode is a major mode that allow us to run a command and see its output result in a special buffer, this resulting buffer show the errors and allow us to navigate through them, you can check the &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;It's a "simple mode" but it can be used for many things like compile and run a program, run tests, and so on.&lt;/p&gt;

&lt;h1&gt;
  
  
  Usage
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Interactive
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;compile&lt;/code&gt; is an interactive function so we can call it with &lt;code&gt;M-x compile&lt;/code&gt; and enter the command we want to execute.&lt;/p&gt;

&lt;p&gt;In the following example we're compiling this blog using hugo binary:&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%2Fqjbwm5dvmsjb3yollok1.gif" 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%2Fqjbwm5dvmsjb3yollok1.gif" alt="run-mx-compile" width="800" height="400"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  From code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;compile&lt;/code&gt; is a emacs-lisp function so we call it from our code, we just need to take care about the &lt;code&gt;default-directory&lt;/code&gt; when we call it, for example if we call it from &lt;code&gt;lib/hello.ex&lt;/code&gt; buffer, &lt;code&gt;default-directory&lt;/code&gt; will be &lt;code&gt;lib&lt;/code&gt; and in some cases we want to use our project root, or a different directory, to run our command.&lt;/p&gt;

&lt;p&gt;To fix this we need to setup &lt;code&gt;default-directory&lt;/code&gt; before we call &lt;code&gt;compile&lt;/code&gt;, for example let's build a custom function to run &lt;code&gt;hlint&lt;/code&gt; in our entire project and then show its results in a &lt;code&gt;compilation&lt;/code&gt; buffer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/run-hlint&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="s"&gt;"Run  hlint over the current project."&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;interactive&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="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;default-directory&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;projectile-project-root&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;compile&lt;/span&gt; &lt;span class="s"&gt;"hlint ."&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case we're setting up &lt;code&gt;default-directory&lt;/code&gt; with our project root (using projectile to get the root) and then when we call &lt;code&gt;compile&lt;/code&gt; it will take &lt;code&gt;default-directory&lt;/code&gt; correctly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Some tweaks
&lt;/h1&gt;

&lt;p&gt;These modifications to default behaviour of &lt;code&gt;compilation-mode&lt;/code&gt; should be made after the mode was loaded so we need to use &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Hooks-for-Loading.html" rel="noopener noreferrer"&gt;with-eval-after-load&lt;/a&gt; otherwise these changes won't be applied correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evil-mode
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;compilation&lt;/code&gt; buffer has some preset key bindings that conflict with &lt;code&gt;evil-mode&lt;/code&gt;, for example when we press &lt;code&gt;g&lt;/code&gt; in a compilation buffer this will re-run the command, but this key binding is also used by &lt;code&gt;evil-mode&lt;/code&gt;, to fix this we can disable the default key binding with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with-eval-after-load&lt;/span&gt; &lt;span class="ss"&gt;'compile&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;define-key&lt;/span&gt; &lt;span class="nv"&gt;compilation-mode-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;"g"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;define-key&lt;/span&gt; &lt;span class="nv"&gt;compilation-mode-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ss"&gt;'recompile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;define-key&lt;/span&gt; &lt;span class="nv"&gt;compilation-mode-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case &lt;code&gt;h&lt;/code&gt; key binding is also disabled (also used by evil) and &lt;code&gt;r&lt;/code&gt; is remapped to &lt;code&gt;recompile&lt;/code&gt; for easy access now that we disabled &lt;code&gt;g&lt;/code&gt; default key binding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow compilation output
&lt;/h2&gt;

&lt;p&gt;By default &lt;code&gt;compilation-mode&lt;/code&gt; doesn't follow the output of the command so if our command result has a large output we'll need to scroll manually, to fix this we can change &lt;code&gt;compilation-scroll-output&lt;/code&gt; to &lt;code&gt;t&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with-eval-after-load&lt;/span&gt; &lt;span class="ss"&gt;'compile&lt;/span&gt;
  &lt;span class="c1"&gt;;; set cursor to follow compilation output&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;compilation-scroll-output&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enable ANSI colors
&lt;/h2&gt;

&lt;p&gt;Some tools show results with colors for easy reading but &lt;code&gt;compilation-mode&lt;/code&gt; won't show them by default, you can make them look better with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;'ansi-color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;colorize-compilation-buffer&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="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;inhibit-read-only&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ansi-color-apply-on-region&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;point-min&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;point-max&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-hook&lt;/span&gt; &lt;span class="ss"&gt;'compilation-filter-hook&lt;/span&gt; &lt;span class="ss"&gt;'colorize-compilation-buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was taken from this &lt;a href="https://stackoverflow.com/questions/3072648/cucumbers-ansi-colors-messing-up-emacs-compilation-buffer/3072831#3072831" rel="noopener noreferrer"&gt;Stack Overflow answer&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Re run compilation from another buffer
&lt;/h2&gt;

&lt;p&gt;When we're making changes in our code we want to re-run our compilation process right after we save the changes but to do this we have to move to the compilation buffer to be able to re-run the compilation, a better approach to do this could be just call &lt;code&gt;recompile&lt;/code&gt; by using a key binding, I use &lt;code&gt;evil-leader&lt;/code&gt; to make this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;evil-leader/set-key&lt;/span&gt; &lt;span class="s"&gt;"R"&lt;/span&gt; &lt;span class="ss"&gt;'recompile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Foed9hmpq8j8lfmtjfq3h.gif" 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%2Foed9hmpq8j8lfmtjfq3h.gif" alt="run-recompile" width="2240" height="1230"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;But it can be attached to any key binding, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;global-set-key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;kbd&lt;/span&gt; &lt;span class="s"&gt;"C-c C-r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ss"&gt;'recompile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: run parrot mode animation when a compilation is successful
&lt;/h2&gt;

&lt;p&gt;I configured &lt;a href="https://github.com/dp12/parrot" rel="noopener noreferrer"&gt;parrot-mode&lt;/a&gt; to animate the little parrot every time the compilation process is a success, to make this we need a small function that check if it was a success and then we need to attach it to &lt;code&gt;'compilation-finish-functions&lt;/code&gt;, this is a variable defined in &lt;code&gt;compilation-mode&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;my/parrot-animate-when-compile-success&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;buffer&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string-match&lt;/span&gt; &lt;span class="s"&gt;"^finished"&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;parrot-start-animation&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="nv"&gt;parrot&lt;/span&gt;
  &lt;span class="ss"&gt;:ensure&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
  &lt;span class="ss"&gt;:config&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;parrot-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-to-list&lt;/span&gt; &lt;span class="ss"&gt;'compilation-finish-functions&lt;/span&gt; &lt;span class="ss"&gt;'my/parrot-animate-when-compile-success&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;As we can see &lt;code&gt;compilation-mode&lt;/code&gt; is a simple but powerful mode that allow us to build our own tools, we can create an automatic build system in case there is not something already existing for the technology we're using or if we just want to run some tasks in an easier way.&lt;/p&gt;

</description>
      <category>emacs</category>
      <category>elisp</category>
    </item>
    <item>
      <title>Things I wish I knew when I started in elixir development</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Mon, 01 Mar 2021 14:54:15 +0000</pubDate>
      <link>https://dev.to/erickgnavar/things-i-wish-i-knew-when-i-started-in-elixir-development-3l11</link>
      <guid>https://dev.to/erickgnavar/things-i-wish-i-knew-when-i-started-in-elixir-development-3l11</guid>
      <description>&lt;p&gt;These are some things I commonly use in my elixir development workflow that might be interesting for someone.&lt;/p&gt;

&lt;h1&gt;
  
  
  Managing multiple versions of elixir and erlang
&lt;/h1&gt;

&lt;p&gt;When you have to work in more than one project at time that could probably means you have to handle different elixir and erlang versions so installing the default version that your OS provides won't be helpful. Here is where &lt;code&gt;asdf&lt;/code&gt; shines to solves this problem, &lt;code&gt;asdf&lt;/code&gt; allow us to have different versions of elixir, erlang and other languages in the same machine so we can easily switch between them.&lt;/p&gt;

&lt;p&gt;In macOS you can install it with &lt;code&gt;brew install asdf&lt;/code&gt; and then follow the instructions that the installer prints out to set up the &lt;code&gt;PATH&lt;/code&gt;, more info in its Github page &lt;a href="https://github.com/asdf-vm/asdf" rel="noopener noreferrer"&gt;https://github.com/asdf-vm/asdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have &lt;code&gt;asdf&lt;/code&gt; installed we need to install the plugins to handle &lt;code&gt;erlang&lt;/code&gt; and &lt;code&gt;elixir&lt;/code&gt;, we can install them with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asdf plugin add erlang
asdf plugin add elixir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have those installed we need to define which versions we're going to use in our project, there is more than one way to do that:&lt;/p&gt;

&lt;h2&gt;
  
  
  Using environment variables
&lt;/h2&gt;

&lt;p&gt;We can set up the required versions by defining environment variables with the &lt;code&gt;ASDF_&lt;/code&gt; prefix so if we need version elixir &lt;code&gt;1.10&lt;/code&gt; we need to define the variable &lt;code&gt;ASDF_ELIXIR_VERSION&lt;/code&gt; with the value &lt;code&gt;1.10&lt;/code&gt; the same applies for erlang or other programming languages as well.&lt;/p&gt;

&lt;p&gt;For example we can define variables for elixir and erlang as the example below:&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;ASDF_ELIXIR_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.10.3-otp-22
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ASDF_ERLANG_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;To handle environment variables in a easy way we can use &lt;a href="https://direnv.net" rel="noopener noreferrer"&gt;direnv&lt;/a&gt;, it allows to define environment variables in a file &lt;code&gt;.envrc&lt;/code&gt; and it will loaded automatically as soon as we enter to out project folder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  File based config file
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;asdf&lt;/code&gt; allow us to define a &lt;code&gt;.tool-versions&lt;/code&gt; file where we can put all the versions needed for our project, we can define one as the example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;erlang 23.0.2
elixir 1.10.4-otp-23
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a new project
&lt;/h2&gt;

&lt;p&gt;Because we have first to define the versions used in a project we can't just run &lt;code&gt;mix new my_app&lt;/code&gt; because &lt;code&gt;asdf&lt;/code&gt; doesn't know yet which versions we want. To do this we have 2 options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define global versions of elixir and erlang using for example &lt;code&gt;asdf global elixir 1.9.0&lt;/code&gt; and the same for erlang &lt;code&gt;asdf global erlang 22.3&lt;/code&gt; and then we can execute &lt;code&gt;mix new my_app&lt;/code&gt; normally&lt;/li&gt;
&lt;li&gt;Define the versions just for the &lt;code&gt;mix new&lt;/code&gt; command execution, for example &lt;code&gt;ASDF_ELIXIR_VERSION=1.9.0 ASDF_ERLANG_VERSION=22.3 mix new my_app&lt;/code&gt;, this way we don't affect the global scope and then we can define these same versions inside the created project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like the second one because I don't have to change the global version each time I want to create a new project and I can easily access to that command from bash history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes about erlang compilation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;asdf&lt;/code&gt; uses &lt;code&gt;kerl&lt;/code&gt; under the hood to handle erlang compilation and when we are installing a new version it will ask for a java installation 😕, to avoid this behaviour we can define the following environment variable:&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;KERL_CONFIGURE_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"--disable-debug --without-javac"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Ecto database url
&lt;/h1&gt;

&lt;p&gt;If we are dealing with databases in our project we will probably be using Ecto. And Ecto allow us to define database credentials in two ways, the first one is define them separately as the example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;database:&lt;/span&gt; &lt;span class="s2"&gt;"ecto_simple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;username:&lt;/span&gt; &lt;span class="s2"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;password:&lt;/span&gt; &lt;span class="s2"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;hostname:&lt;/span&gt; &lt;span class="s2"&gt;"localhost"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the second one is using a unique parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;url:&lt;/span&gt; &lt;span class="s2"&gt;"postgres://postgres:postgres@localhost/ecto_simple"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is my favorite option for these reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Just one value to maintain&lt;/li&gt;
&lt;li&gt;This format is also accepted in &lt;code&gt;psql&lt;/code&gt;, for example we can execute &lt;code&gt;psql postgres://postgres:postgres@localhost/ecto_simple&lt;/code&gt; and we're connected to the database. I just discovered this a few weeks ago 😅&lt;/li&gt;
&lt;li&gt;We can change credentials for example when we're running a mix command just prepending the value &lt;code&gt;DATABASE_URL=postgres://postgres:postgres@localhost/test_db mix something&lt;/code&gt; in the case we're loading it from an environment variable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then if you have the connection url in a variable called &lt;code&gt;DATABASE_URL&lt;/code&gt;, using &lt;code&gt;direnv&lt;/code&gt; of course 😉, you can just execute &lt;code&gt;psql $DATABASE_URL&lt;/code&gt; to database session.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using iex
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Enable shell history
&lt;/h2&gt;

&lt;p&gt;A cool feature of elixir is &lt;code&gt;iex&lt;/code&gt;, you can load modules, recompile them and so on, but when sometimes we execute "large" pieces of code or some cases that we're trying out to understand the code or something else and when we have to restart the session we lost all the history 😢, we can avoid this by adding a flag &lt;code&gt;-kernel shell_history enabled&lt;/code&gt; in the environment variable &lt;code&gt;ERL_AFLAGS&lt;/code&gt; before we start our &lt;code&gt;iex&lt;/code&gt; session. I just put following code in my &lt;code&gt;.zshrc&lt;/code&gt; to have it enabled for all my projects:&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;ERL_AFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-kernel shell_history enabled"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preload aliases
&lt;/h2&gt;

&lt;p&gt;Another thing than could be annoying to deal with is aliasing a large module name, for example if we have &lt;code&gt;MyApp.Contexts.Authentication.User&lt;/code&gt; and we are using this module pretty often it could be easier to have it already loaded when we start a iex session, we can make this by defining a &lt;code&gt;.iex.exs&lt;/code&gt; file in the project root with the desired aliases, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Contexts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Authentication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now when we start a new &lt;code&gt;iex&lt;/code&gt; session we will have that module aliased from the beginning so we can use &lt;code&gt;User.whatever&lt;/code&gt; without a problem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep in mind that even if we can make an alias(a module name is just an atom) when we starting a session using just &lt;code&gt;iex&lt;/code&gt; we cannot access to its functions. We need to start our &lt;code&gt;iex&lt;/code&gt; session using &lt;code&gt;iex -S mix&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Recompiling modules
&lt;/h2&gt;

&lt;p&gt;Within a &lt;code&gt;iex&lt;/code&gt; sessions we can recompile a module just writing &lt;code&gt;r module_name&lt;/code&gt; and if the want to recompile the whole project we can execute &lt;code&gt;recompile&lt;/code&gt;, this is useful when we are making some changes in the code and we need to test it right away with all the values that we already had defined. It's also called "REPL based development" and it's most used with lisp based programming languages but having &lt;code&gt;iex&lt;/code&gt; in elixir we can use those nice features as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  Mix tasks
&lt;/h1&gt;

&lt;p&gt;These are tasks that &lt;code&gt;mix&lt;/code&gt; can run, duhh.. But we can create them and use them in our projects. For example maybe we are debugging some code and we don't want to execute a long process(business process) instead of that we can just extract some function calls and execute them from a mix task using existing data. We can create a mix task with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Tasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Foo&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@moduledoc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Task&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ensure_all_started&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"runnning..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have to name this file &lt;code&gt;foo.ex&lt;/code&gt; and place it inside &lt;code&gt;lib&lt;/code&gt; folder and now we can run &lt;code&gt;mix foo&lt;/code&gt; and we'll get a &lt;code&gt;running...&lt;/code&gt; message.&lt;/p&gt;

&lt;p&gt;I use this many times, actually I have some defined tasks in many projects than I reuse to debug some workflows.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I know that we "should" be defining the cases that we are debugging in a test, run it and then try to fix the code and then run the tests again but this way works for me so I'm OK with that 🙃&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Working with local third party libraries
&lt;/h1&gt;

&lt;p&gt;In some cases we could found some weird behaviour, a bug of just want to know a little more deep about how a third party library works. In that case it could be difficult to setup a local version of a library that we use in our project.&lt;/p&gt;

&lt;p&gt;I remember using just &lt;code&gt;pip install -e path_to_library&lt;/code&gt; in python and just starting to changing the library code.&lt;/p&gt;

&lt;p&gt;In elixir when we want to install a local version of a library we can specify the path of it in the &lt;code&gt;mix.exs&lt;/code&gt; file, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;MixProject&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Project&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="ss"&gt;app:&lt;/span&gt; &lt;span class="ss"&gt;:my_app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;version:&lt;/span&gt; &lt;span class="s2"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;elixir:&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;deps:&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ecto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;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="ss"&gt;:postgrex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.8.1"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ecto_sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;path:&lt;/span&gt; &lt;span class="s2"&gt;"ecto_sql_local_path"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case we're telling our project to install &lt;code&gt;ecto_sql&lt;/code&gt; from the given &lt;code&gt;path&lt;/code&gt;, this will work but just the first time because it will load and compile &lt;code&gt;ecto_sql&lt;/code&gt; at the beginning and then when we're making some changes in the code placed in &lt;code&gt;ecto_sql_local_path&lt;/code&gt; these changes won't be recompiled automatically because &lt;code&gt;mix&lt;/code&gt; is only watching for changes inside our project. In this case we can force to recompile some modules by using for example &lt;code&gt;r Ecto.Migrator&lt;/code&gt; from within an &lt;code&gt;iex&lt;/code&gt; session but if we are modifying more modules it would be tedious to recompile manually every one of them, for this case we can define a &lt;code&gt;Recompiler&lt;/code&gt; module that make this work for us, name it as you want, this will contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Recompiler&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;modules_to_recompile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;SomeOtherModule&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;for&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;modules_to_recompile&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;IEx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can place this module somewhere inside our &lt;code&gt;lib&lt;/code&gt; folder and when we call &lt;code&gt;Recompiler.run&lt;/code&gt; from within a &lt;code&gt;iex&lt;/code&gt; session it will recompile all the defined modules.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>erlang</category>
    </item>
    <item>
      <title>Activate python virtualenv automatically with direnv</title>
      <dc:creator>Erick 🙃 Navarro</dc:creator>
      <pubDate>Mon, 01 Mar 2021 05:06:47 +0000</pubDate>
      <link>https://dev.to/erickgnavar/activate-python-virtualenv-automatically-with-direnv-4kgg</link>
      <guid>https://dev.to/erickgnavar/activate-python-virtualenv-automatically-with-direnv-4kgg</guid>
      <description>&lt;p&gt;&lt;a href="https://direnv.net" rel="noopener noreferrer"&gt;direnv&lt;/a&gt; is a tool to set up automatically environment variables as soon as we enter in a directory that contains a &lt;code&gt;.envrc&lt;/code&gt; file. We can use this feature to activate our virtualenvs as well.&lt;/p&gt;

&lt;p&gt;Let's see what happens when we activate manually a virtualenv with &lt;code&gt;source ./env/bin/activate&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A new new environment variable called &lt;code&gt;VIRTUAL_ENV&lt;/code&gt; is exported.&lt;/li&gt;
&lt;li&gt;  The path is updated to include the &lt;code&gt;bin&lt;/code&gt; directory inside our &lt;code&gt;virtualenv&lt;/code&gt; this is made to allow us to point to the correct python installation and run cli interfaces exposed by the dependencies we have installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because all the "magic" about activating a &lt;code&gt;virtualenv&lt;/code&gt; is basically configuring some environment variables we can do it automatically using &lt;code&gt;direnv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's assume we have a &lt;code&gt;virtualenv&lt;/code&gt; installed in the path &lt;code&gt;/Users/erick/.virtualenvs/demo&lt;/code&gt;, the &lt;code&gt;virtualenv&lt;/code&gt; is located inside &lt;code&gt;~/.environments&lt;/code&gt; because I'm using &lt;code&gt;virtualenvwrapper&lt;/code&gt; but it can be in any other location. Now we can use this location to configure our &lt;code&gt;.envrc&lt;/code&gt; file as the following:&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;VIRTUAL_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/Users/erick/.virtualenvs/demo
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/Users/erick/.virtualenvs/demo/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we enter our project folder the virtualenv will be "activated" automatically and when we leave the project folder it will be "deactivated".&lt;/p&gt;

&lt;p&gt;Also when we use this method is easiest for our editor(emacs in my case) to recognize the current python installation and be able to run tests, execute files, etc.&lt;/p&gt;

</description>
      <category>python</category>
      <category>virtualenv</category>
      <category>direnv</category>
    </item>
  </channel>
</rss>
