<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	   xmlns:content="http://purl.org/rss/1.0/modules/content/"
	   xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	   xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	   xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	   xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	   xmlns:georss="http://www.georss.org/georss"
     xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
     xmlns:media="http://search.yahoo.com/mrss/"><channel>
  <title>yannesposito.com</title>
  <atom:link href="https://yannesposito.com/rss.xml" rel="self" type="application/rss+xml" />
  <link>https://yannesposito.com</link>
  <description><![CDATA[yannesposito.com articles, mostly random personal thoughts]]></description>
  <language>en</language>
  <pubDate>Sun, 21 Dec 2025 00:00:00 +0100</pubDate>
  <lastBuildDate>Sat, 25 Jul 2026 23:24:00 +0200</lastBuildDate>
  <generator>mkrss.sh</generator>
  <webMaster>yann@esposito.host (Yann Esposito)</webMaster>
  <image>
    <url>https://yannesposito.com/img/FlatAvatar.png</url>
    <title>yannesposito.com</title>
    <link>https://yannesposito.com</link>
  </image>

<item>
<title>How I protect my forgejo instance from AI Web
Crawlers</title>
<guid>https://yannesposito.com/posts/0031-how-i-protect-my-forgejo-instance-from-ai-web-crawlers/index.html</guid>
<pubDate>Sun, 21 Dec 2025 00:00:00 +0100</pubDate>
<category>blog</category>
<category>static</category>
<description><![CDATA[
<main id="content"><article><h1 id="tl-dr-">TL;DR:</h1><p>Put that in your nginx config:</p><pre><code>location / {
  # needed to still allow git clone from http/https URLs
  if ($http_user_agent ~* &quot;git/|git-lfs/&quot;) {
        set $bypass_cookie 1;
  }
  # If we see the expected cookie; we could also bypass the blocker page
  if ($cookie_Yogsototh_opens_the_door = &quot;1&quot;) {
        set $bypass_cookie 1;
  }
  # Redirect to 418 if neither condition is met
  if ($bypass_cookie != 1) {
     add_header Content-Type text/html always;
     return 418 &#39;&lt;script&gt;document.cookie = &quot;Yogsototh_opens_the_door=1; Path=/;&quot;; window.location.reload();&lt;/script&gt;&#39;;
  }
  # rest of your nginx config
</code></pre><p>Preferably run a string replace from
<code>Yogsototh_opens_the_door</code> to your own personal Cookie
name.</p><p>Main advantage, is that it is almost invisible to the users of my
website compartively to other solutions like Anubis.</p><h1 id="more-detail">More detail</h1><p>Not so long ago I started to host my code to <a href="https://forgejo.org">forgejo</a>. There is a promise that in the
future it will support federation and forgejo is the same project that
is used for <a href="https://codeberg.org">codeberg</a>.</p><p>The only problem I had was one day, I discovered that my entire node
was down. At first I didn't investigate and just restarted the node. But
soon after a few hours, it was down again. Looking at the reason,
clearly thousands of requests that looked at every commit which put too
much pressure on the system. Who could be so interested in using the web
API to look at every commit instead, of… you know, clone the repository
locally and explore it. Quickly, yep, like so many of you, I discovered
that tons of crawlers that did not respect the <code>robots.txt</code>
are crawling my forgejo instance until death ensues.</p><p>So I had no choice, I first used a radical approach and blocked my
website entirely except from me. But hey, why having a public forge if
not for people to be able to look into it time to time?</p><p>I then installed Anubis, but it wasn't really for me. It is way too
heavy for my needs, not as easy as I would have hoped to configure and
install.</p><p>Then I saw this article <a href="https://fxgn.dev/blog/anubis/">You
don't need anubis</a> on <a href="https://lobste.rs">lobste.rs</a> using
a simple configuration in caddy that should block these pesky crawlers.
I made some adjustments to adapt it to nginx. For now, this is working
perfectly well, my users are just redirected once, without really
noticing it. And they could use forgejo as they could before. And this
puts the crawlers away.</p><p>The strategy is pretty basic; in fact, a lot less advanced than the
strategy adopted by Anubis. For every access of my website, I just check
if the user has a specific cookie set. If not, I redirect the user to a
418 HTML page containing some js code to execute that set this specific
cookie and reload the page.</p><p>That's it.</p><p>I also tried to return a 302 and add a cookie from the response
without javascript, but the crawlers are immune to that second strategy.
Unfortunately this means, my website could only be seen if you enable
javascript in your browser. I feel this is acceptable. I guess, someday
this very basic protection will not be enough and my forgejo instance
will break again, and I will be forced to use more advanced system like
<a href="https://anubis.techaro.lol">Anubis</a> or perhaps even <a href="https://iocaine.madhouse-project.org">iocaine</a>.</p><p>I hope this could be helpful, because, I recently saw many
discussions on that subject where people were not totally happy to use
Anubis, while at least for me, this quick dirty fix does the trick. And
I am fully aware that this would be very easy to bypass. But for now, I
think the volume is more important than the quality for these crawlers
and it may take a while for them to need to adapt. Also, by publishing
this, I know if too many people use the same trick, quickly, these
crawlers will adapt.</p></article></main>
]]></description>
</item>


<item>
<title>AI Assistant return of experience</title>
<guid>https://yannesposito.com/posts/0030-ai-assistant-return-of-experience/index.html</guid>
<pubDate>Thu, 03 Jul 2025 00:00:00 +0200</pubDate>
<category>llm</category>
<category>ai</category>
<category>agent</category>
<category>claude</category>
<category>gpt</category>
<category>chatgpt</category>
<category>copilot</category>
<description><![CDATA[
<main id="content"><article><div class="notes"><p><strong>TL;DR of TL;DR:</strong> With the correct setup LLM-based AI
Assistant have their place in the Software Developer tool-belt.</p></div><div class="notes"><p><strong>TL;DR:</strong></p><ol><li><strong>Evaluations</strong><ul><li>The Good:<ul><li><code class="verbatim">(++)</code> answering specific technical
questions (with references),</li><li><code class="verbatim">(++)</code> write small unreliable scripts
nor prod-level quality (if helped)</li><li><code class="verbatim">(+)</code> simple/repetitive coding tasks,
boilerplate,</li><li><code class="verbatim">(+)</code> review</li><li><code class="verbatim">(+)</code> documentation writing</li></ul></li><li>The Bad:<ul><li><code class="verbatim">(-)</code> test writing</li><li><code class="verbatim">(-)</code> Perform specific, small, and easy
(beginner junior) production-ready programming task.</li></ul></li><li>The Ugly:<ul><li><code class="verbatim">(--)</code> Big tasks that would require a
lot of intermediate steps.</li><li><code class="verbatim">(---)</code> Code a feature with a production
quality. It is a lot faster and more reliable to code manually. Almost
all invested time was lost.</li></ul></li></ul></li><li><strong>Setup:</strong> need to support <strong>AI Agent</strong>,
<strong>MCP</strong> (Clojure-MCP in particular!),
<strong>RAG</strong></li><li><strong>Limitations:</strong> bullshitting, non-verbal reasoning,
complex coding tasks, self reference</li><li><strong>Conclusions:</strong><ul><li>Main lesson: Great for small and easy tasks but you already need
some expertise.</li><li>Using AI Agent is a skill like learning other tools like <code class="verbatim">git</code>.<ul><li>They bring value and are an improvement</li><li>never blindly trust ; always double check the answers</li><li>Learn what you can and cannot delegate</li></ul></li><li>I would say that you can <strong>PARTIALLY</strong> delegate about
20% of the daily tasks; code review, documentation, code analysis.</li><li>WORKS VERY POORLY without advanced latest setup (MCP, RAG,
reasoning, agent behavior), probably cost a lot.</li><li>AI Workflows potentially superior to AI Agents?</li></ul></li></ol></div><h1 id="introduction">Introduction</h1><p>My past encounters with LLMs were far from impressive. So, what made
me decide to give them another chance?</p><ul><li>I saw a <a href="https://github.com/cloudflare/workers-oauth-provider/?tab=readme-ov-file#written-using-claude">GitHub
repository showing a full OAuth2 Provider</a> built using Claude. One
commit per prompt. I didn't follow all commits, but it was nice to see
that this was possible.</li><li><a href="https://github.com/bhauman/clojure-mcp">Clojure-MCP</a> ; I
didn't knew at that time precisely how that could be a game changer, but
it is.</li><li>Heard good things about Claude for programming and I saw a few web
app made from just a few directives.</li></ul><h1 id="the-setup">The Setup</h1><p>Setting things up was not straightforward. But once done I must admit
I was really impressed.</p><p>I feel AI Agent are now like Versioning System (<code class="verbatim">git</code>) and Build Systems (<code class="verbatim">make</code>). These are tools worthy of learning. But
from my experience they only provide value if you have a full setup:</p><ul><li><strong>Agent</strong>: The AI needs to work on its own, handling
several connected tasks without asking for your input every time.</li><li><strong>Clojure-MCP</strong>; it makes these tools way better, even
if you code in another language.</li><li><strong>RAG</strong>; the system must support file/buffer context to
add in order to be able to follow your project best practices.</li></ul><p>See <a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html">my
previous article</a> to see all details of my current setup.</p><h1 id="productivity-gain">Productivity Gain</h1><h2 id="-------hard-advanced-programming"><code class="verbatim">(--)</code> Hard/Advanced Programming</h2><p>First, let's make it clear. For real, production-ready, quality
development or complex tasks with advanced requirements these tools are
a <strong>NET NEGATIVE</strong>.</p><p>If the task at hand is not small in scope, with a clear specification
the system will act as a <em>confidently wrong junior developer</em>. In
the end, most your time will be lost.</p><p>Worse than that, discovering the problems generated by the tools will
not be a trivial task, and in the end result in a lot more time loss
than if you would simply not have used any AI Agent.</p><p>So be conscious about the difficulty of the task at hand.</p><h2 id="------boilerplate"><code class="verbatim">(+)</code>
Boilerplate</h2><p>For writing boilerplate code following your project's best practices
I would say my experience was positive. Some subtle errors could appear,
and this was not great. Still, it is faster to use the AI for the
boilerplate than typing it yourself most of the time.</p><h2 id="-------small-disposable-scripts-that-don't-need-to-be-reliable"><code class="verbatim">(++)</code> Small disposable scripts that don't need to
be reliable</h2><p>If you need to write a script that don't need to be highly reliable.
In that case, I can say my experience was a <strong>BIG NET
POSITIVE</strong>. It worked because the tasks to write this script
was:</p><ul><li>small in scope</li><li>clear technical design</li></ul><p>It built he tool for me in a few seconds which would have taken me
more time just typing it.</p><p>An important remark however, is that if you know what would be the
"Best tool for the work", this would also tremendously improve the speed
and quality of the result.</p><p>I asked the Agent to build the same tool twice. First in bash, second
using <a href="https://babashka.org">babashka</a>. And for my specific
need, not only it was a lot faster to build the tool using babashka, but
the result was clearly superior both in design and quality as well as
efficiency.</p><h2 id="------pr-reviewing"><code class="verbatim">(+)</code> PR
Reviewing</h2><p>I think a nice use case of AI Assistant is to ask it to generate a
review of your PR locally prior to send it to show it to your team.</p><ul><li>about 10% is plain wrong and MUST be ignored</li><li>70% not useful, but could be ignored,</li><li><strong>20%</strong> are good recommendations.</li></ul><p>While 20% is not much, this is still a net positive. Personally I
feel that using AI just for review would already be a good reason to add
them to your tool-belt.</p><h2 id="-------asking-specific-technical-details-about-your-own-project"><code class="verbatim">(++)</code> Asking specific technical details about
your own project</h2><p>Using RAG + Clojure-MCP to search and discover the details of your
Project, this ended up being very useful.</p><p>I work on a huge base code. We created a full platform from scratch
that contains hundreds of technical details. Some of them are long
forgotten in some documentation, and some were not even cleanly
documented due to time pressure during the delivery.</p><p>Still I could ask these very specific questions and got a clear
answer. Of course, as you should never trust the system fully, I asked
for the source code and documentation to be able to verify the answer.
And it worked perfectly.</p><p>Typically I could ask "What is the HTTP Header to use for this
specific feature?" and it looks at the code and returns the value with a
small documentation and pointer to the sources.</p><h2 id="------writing-doc"><code class="verbatim">(+)</code> Writing
doc</h2><p>While everything must be proofread because <strong>you cannot fully
trust the Agent</strong>, this generally gives a good first pass
overview and can generate a documentation of mediocre quality. If you
want a good documentation it will need a lot more time. But I still give
a positive outcome as existing and bad documentation is still a net
positive comparatively to no documentation.</p><h2 id="------writing-tests"><code class="verbatim">(-)</code> Writing
tests</h2><p>In my experience, asking these agent to write tests can either be a
net positive or a full net negative were I had to delete all generated
tests. So I wouldn't recommend using AI code generation for writing
advanced tests cases. But I feel this is fine for very specific small
functions. And even there, you really need to be cautious about passing
tests that are not useful at all and just give a false sense of
safety.</p><p>Sometime it is just better to write the tests, at least the cases but
not the boilerplate, yourself to be sure you are testing the correct
thing.</p><h2 id="-------familiarize-with-foreign-code"><code class="verbatim">(++)</code> Familiarize with foreign code</h2><p>The ability to generate a <code class="verbatim">PROJECT_SUMMARY.md</code> as suggested by the
Clojure-MCP tutorial, proves that these tool can really help grasp a new
project. It's structure, the generic ideas, etc…</p><p>This is not totally perfect, but I got impressive results, so this
count as <strong>NET POSITIVE</strong>.</p><h1 id="conclusions">Conclusions</h1><p>With this new tryout of the AI tools I could discover tasks for which
they are useful. In terms of productivity, I believe their benefits
depend tremendously on the type of work you assign them. I feel they
have a place as a daily-use tool, particularly for pre-reviewing your
code and helping you write documents.</p><p>To be effective you need to:</p><ol><li>Use something at least as efficient as <em>Claude 4 Sonnet</em>; so
an <strong>Agent</strong> with <strong>Clojure-MCP</strong> and
<strong>RAG</strong> support.</li><li>Be conscious about the <strong>scope</strong> and
<strong>complexity</strong> of the task</li><li>Be able to provide <strong>precise technical specification</strong>
of the task</li><li>Always be conscious (evaluate and monitor) the time it would take to
complete the task manually versus the time spent supervising the
automated tool.</li></ol><p>If you familiarize yourself with these agents, really try
Clojure-MCP. It is a game changer and that provide a pretty huge
advantage to Clojure comparatively to other programming languages.</p></article></main>
]]></description>
</item>


<item>
<title>AI Assistants setup in Emacs &amp; Clojure-MCP</title>
<guid>https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html</guid>
<pubDate>Tue, 17 Jun 2025 00:00:00 +0200</pubDate>
<category>ai</category>
<category>llm</category>
<category>emacs</category>
<category>mcp</category>
<category>clojure</category>
<category>setup</category>
<category>claude</category>
<category>copilot</category>
<category>gptel</category>
<category>doom-emacs</category>
<description><![CDATA[
<main id="content"><article><div class="notes"><p>This article is about how to setup Emacs (doom-emacs in particlar) to
work with most AI Assitants for Clojure projects using Clojure-MCP to
provide the AI assistant the ability to run commands in the Clojure
REPL.</p></div><h1 id="introduction">Introduction</h1><p>By the end of this article, you'll be able to give instructions to
the AI Assistant and it will use the REPL of your specific project to
execute them. For example, I asked the assistant to list all users in my
project's database (running locally, of course). With my setup, the
assistant could:</p><ul><li>analyze the project (source and document files)</li><li>discover the service and functions that could perform the operation
to list all users</li><li>ran the code in the repl, and got back all users</li><li>used this answer to group the users (by org which makes sense for my
project)</li><li>rendered the result as an org-mode file.</li></ul><p>I ran into a few bumps along the way since this setup uses some
pretty cutting-edge tools and libraries. It took me a bit of time to
work through these issues, which is exactly why I'm putting this article
together - partly to help others who might face the same challenges, and
partly as a reference for my future self.</p><p>If you follow this guide you should endup with a fully working setup
with: Emacs + AI Assitant + Clojure-MCP development environment.</p><ul><li><strong><strong>Platform</strong></strong>: macOS</li><li><strong><strong>Editor</strong></strong>: Emacs Plus 31 with Doom
Emacs</li><li><strong><strong>Emacs packages</strong></strong>:<ul><li><a href="https://github.com/karthink/gptel"><code class="verbatim">gptel</code></a> for LLM interaction</li><li><a href="https://github.com/lizqwerscott/mcp.el">mcp</a> (Model
Context Protocol) support</li></ul></li><li><strong><strong>Development Stack</strong></strong>:<ul><li><a href="https://github.com/bhauman/clojure-mcp"><code class="verbatim">clojure-mcp</code></a> server</li><li>Custom MCP startup scripts for project integration</li></ul></li></ul><h1 id="steps">Steps</h1><h2 id="emacs">Emacs</h2><h3 id="install-latest-emacs-plus">Install latest emacs-plus</h3><div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">brew</span> install emacs-plus <span class="at">--with-native-comp</span> <span class="at">--with-modern-black-dragon-icon</span></span></code></pre></div><ol><li><p>emacs-plus and native-comp shenanigans</p><p>Currently to fix emacs-plus from brew on macOS native-comp, add this
to the beginning of your <code class="verbatim">~/.emacs.d/early-init.el</code>.</p><div class="sourceCode" id="cb2" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb2-1"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">;; FIX NATIVE COMP: https://github.com/d12frosted/homebrew-emacs-plus/issues/554#issuecomment-2909894593</span></span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a>(setenv <span class="st">&quot;LIBRARY_PATH&quot;</span></span>
<span id="cb2-3"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb2-3" aria-hidden="true" tabindex="-1"></a>        (mapconcat <span class="dt">&#39;identity</span></span>
<span id="cb2-4"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb2-4" aria-hidden="true" tabindex="-1"></a>                   &#39;(<span class="st">&quot;/opt/homebrew/opt/gcc/lib/gcc/15&quot;</span></span>
<span id="cb2-5"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb2-5" aria-hidden="true" tabindex="-1"></a>                     <span class="st">&quot;/opt/homebrew/opt/libgccjit/lib/gcc/15&quot;</span></span>
<span id="cb2-6"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb2-6" aria-hidden="true" tabindex="-1"></a>                     <span class="st">&quot;/opt/homebrew/opt/gcc/lib/gcc/15/gcc/aarch64-apple-darwin24/15&quot;</span>)</span>
<span id="cb2-7"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb2-7" aria-hidden="true" tabindex="-1"></a>                   <span class="st">&quot;:&quot;</span>))</span></code></pre></div></li></ol><h3 id="doom-emacs">doom-emacs</h3><p>As I started from an already existing <code>doom-emacs</code>
installation. I will not cover that part. Please refer to the doom-emacs
installation <a href="https://github.com/doomemacs/doomemacs?tab=readme-ov-file#install">project
documentation</a> if you want to install it .</p><ol><li><p>Declare the new packages to use</p><p>In <code class="verbatim">init.el</code>:</p><pre class="elisp"><code>...
:tools
  ...
  llm ;; &lt;- uncomment this line
  ...
</code></pre><p>In <code class="verbatim">~/.doom.d/packages.el</code>:</p><pre class="elisp"><code>(package! mcp
    :pin &quot;3a380185597f56290057f370ab65dde89a20a18f&quot;)
</code></pre><p>Then configure the packages:</p><p>In <code class="verbatim">~/.doom.d/config.el</code>:</p><pre class="elisp"><code>;; LLM
(use-package! gptel
  :init
  ;; declare the default llm configuration
  ;; for me this is co-pilot with claude-sonnet-4
  (setq gptel-default-mode &#39;org-mode ;; I want the llm to reply to me with org-mode format.
        gptel-backend (gptel-make-gh-copilot &quot;Copilot&quot;)
        gptel-model &#39;claude-sonnet-4)
  (require &#39;gptel-integrations) ;; &lt;- for mcp
  ;; declare another llm configuration, I also use GPT4All to consume locally only
  (gptel-make-gpt4all &quot;GPT4All&quot;
                      :protocol &quot;http&quot;
                      :host &quot;localhost:4891&quot;
                      ;; &quot;~/Library/Application Support/nomic.ai/GPT4All&quot;
                      :models &#39;(Meta-Llama-3-8B-Instruct.Q4_0.gguf)))

(use-package! mcp
  :after gptel
  :config (require &#39;mcp-hub)
  :init (setq mcp-hub-servers
              ;; this is the important block here
              ;; it tells that it can launch an MCP server
              ;; for my-project by running the following
              ;; command line:
              ;;    clojure-mcp my-project
              &#39;((&quot;my-project&quot; :command &quot;clojure-mcp&quot; :args (&quot;my-project&quot;))))
  :hook (after-init . mcp-hub-start-all-server))
</code></pre></li><li><p>Rebuild / Install doom-emacs</p><p>After using another emacs version you need to rebuild your emacs
packages.</p><div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ex">doom</span> sync <span class="at">-u</span></span>
<span id="cb6-2"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="ex">doom</span> rebuild</span></code></pre></div><p>This will take a while.</p></li></ol><h2 id="clojure-mcp">Clojure-MCP</h2><h3 id="update-your-global-clojure-aliases">Update your global Clojure
aliases</h3><p>The file <code class="verbatim">~/.clojure/deps.edn</code>:</p><div class="sourceCode" id="cb7"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb7-1"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:aliases</span></span>
<span id="cb7-2"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:mcp</span></span>
<span id="cb7-3"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-3" aria-hidden="true" tabindex="-1"></a>  {<span class="at">:deps</span> {org.slf4j/slf4j-nop {<span class="at">:mvn/version</span> <span class="st">&quot;2.0.16&quot;</span>} <span class="co">;; Required for stdio server</span></span>
<span id="cb7-4"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-4" aria-hidden="true" tabindex="-1"></a>          com.bhauman/clojure-mcp {<span class="at">:git/url</span> <span class="st">&quot;https://github.com/bhauman/clojure-mcp.git&quot;</span></span>
<span id="cb7-5"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-5" aria-hidden="true" tabindex="-1"></a>                                   <span class="at">:git/tag</span> <span class="st">&quot;v0.1.4-alpha&quot;</span></span>
<span id="cb7-6"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-6" aria-hidden="true" tabindex="-1"></a>                                   <span class="at">:git/sha</span> <span class="st">&quot;080b29b&quot;</span>}}</span>
<span id="cb7-7"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-7" aria-hidden="true" tabindex="-1"></a>   <span class="at">:exec-fn</span> clojure-mcp.main/start-mcp-server</span>
<span id="cb7-8"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-8" aria-hidden="true" tabindex="-1"></a>   <span class="at">:exec-args</span> {<span class="at">:port</span> <span class="dv">7888</span>}}}</span>
<span id="cb7-9"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb7-9" aria-hidden="true" tabindex="-1"></a> }</span></code></pre></div><h3 id="create-the-command-line-to-launch-the-clojure-mcp-server">Create
the command line to launch the Clojure MCP Server</h3><div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env bash</span></span>
<span id="cb8-2"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-3"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="cf">case</span> <span class="va">$1</span> <span class="kw">in</span></span>
<span id="cb8-4"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-4" aria-hidden="true" tabindex="-1"></a>    <span class="ss">my-project</span><span class="kw">)</span> <span class="va">PORT</span><span class="op">=</span>7888<span class="cf">;;</span></span>
<span id="cb8-5"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-5" aria-hidden="true" tabindex="-1"></a>    <span class="pp">*</span><span class="kw">)</span> <span class="bu">echo</span> <span class="st">&quot;I don&#39;t know how to start this project (</span><span class="va">$1</span><span class="st">)&quot;</span> <span class="op">&gt;&amp;</span><span class="dv">2</span></span>
<span id="cb8-6"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-6" aria-hidden="true" tabindex="-1"></a>       <span class="bu">exit</span> 1<span class="kw">;</span></span>
<span id="cb8-7"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="cf">esac</span></span>
<span id="cb8-8"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-9"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="ex">clojure</span> <span class="at">-X:mcp</span> <span class="at">--port</span> <span class="va">$PORT</span></span></code></pre></div><h3 id="link-clojure-mcp-to-your-clojure-project">Link Clojure MCP to
your Clojure project</h3><p>It is important to perform the operation in the correct order. So
<strong>FIRST</strong> go to your project and start the repl. Personally
I work on a very big professional Clojure project that started even
before <code class="verbatim">deps.edn</code> was thing in Clojure. So I
could start the repl with a lein command:</p><div class="sourceCode" id="cb9"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb9-1"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="ex">lein</span> repl :start :port 7888</span></code></pre></div><p>Got it! Here's your project setup in a friendlier tone:</p><p>Since my project is pretty large and has its own restart helpers, I
don't use the headless mode that the clojure-mcp docs suggest. Instead,
I fire up the project with :start mode so I can manually load all the
namespaces and get everything running with a the <code>(reset!)</code>
command.</p><p>Once my project is running (after <code>reset!</code>) <strong>ONLY
THEN</strong> I start the Clojure mcp server.</p><p>I could test that by running my <code>clojure-mcp my-project</code>
command, and I get back a list of JSON objects lines:</p><div class="sourceCode" id="cb10"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;</span> clojure-mcp <span class="ex">my-project</span></span>
<span id="cb10-2"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="dt">{</span><span class="st">&quot;jsonrpc&quot;</span><span class="dt">:</span><span class="st">&quot;2.0&quot;</span><span class="op">,</span><span class="st">&quot;method&quot;</span><span class="dt">:</span><span class="st">&quot;notifications/tools/list_changed&quot;</span><span class="dt">}</span></span>
<span id="cb10-3"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="ex">...</span></span>
<span id="cb10-4"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="dt">{</span><span class="st">&quot;jsonrpc&quot;</span><span class="dt">:</span><span class="st">&quot;2.0&quot;</span><span class="op">,</span><span class="st">&quot;method&quot;</span><span class="dt">:</span><span class="st">&quot;notifications/resources/list_changed&quot;</span><span class="dt">}</span></span>
<span id="cb10-5"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="ex">...</span></span>
<span id="cb10-6"><a href="https://yannesposito.com/posts/0029-ai-assistants-in-doom-emacs-31-on-macos-with-clojure-mcp-server/index.html#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="dt">{</span><span class="st">&quot;jsonrpc&quot;</span><span class="dt">:</span><span class="st">&quot;2.0&quot;</span><span class="op">,</span><span class="st">&quot;method&quot;</span><span class="dt">:</span><span class="st">&quot;notifications/prompts/list_changed&quot;</span><span class="dt">}</span></span></code></pre></div><p>So this is a good sign.</p><h2 id="using-it">Using it</h2><p>Now, everything should be ready to use.</p><p>After starting my repl, I launch emacs (if emacs was already started,
you probably need to restart your mcp server)</p><p>Once emacs is started, open gptel menu (<code>SPC o l m</code>). Try
to add tools (if they are not there ; type <code>M+</code> and start the
mcp if not started)</p><p>Once done, here you go.</p><p><code>SPC o l l</code> => will create a new buffer to send orders
to the LLM. I started with:</p><blockquote><p>Analyze the codebase structure, Document key files, dependencies and
available tools. Generate or update a comprehensive documentation in a
format optimized for LLM Assistants. Save this documentation in the file
<code class="verbatim">PROJECT_SUMMARY.md</code>.</p></blockquote><p>This should generate this <code class="verbatim">PROJECT_SUMMARY.md</code> file and you should generally
always add this file to your LLM session context when working on your
project.</p><p>Now, to show how powerful this setup is, I could ask:</p><p>> Can you list all the users in the database?</p><p>And the AI Assistant (it was claude) was able to analyse the project,
discover the service to use, call the correct methods of this service to
list all users in my local environment DB. The result was then rendered
as a nice org-mode file where users were grouped by Org. This was even
better that what I could expect.</p><p>I could als ask how he could perform this operation, and the
assistant returned the commands he exectued in the repl.</p></article></main>
]]></description>
</item>


<item>
<title>ob-metapost</title>
<guid>https://yannesposito.com/posts/0028-ob-metapost/index.html</guid>
<pubDate>Thu, 19 Dec 2024 00:00:00 +0100</pubDate>
<category>emacs</category>
<category>metapost</category>
<description><![CDATA[
<main id="content"><article><p>When I searched how to generate pictures in org-mode on the Internet,
Metapost is surprisingly missing.</p><p>If you never tried Metapost, I think you should really take a look at
this gem.</p><p>This article will simply provide an example how I could create an
emacs package (not yet distributed) to use Metapost in my org-mode
documents.</p><h1 id="the-script">The script</h1><p>One of the main issue with Metapost is that, like
<math display="inline" xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>L</mi><mi>A</mi></msup><msub><mi>T</mi><mi>E</mi></msub><mi>X</mi></mrow><annotation encoding="application/x-tex">L^AT_EX</annotation></semantics></math>,
it generates a bunch of temporary files and clutter your current
directory with them. There is also a notion that Metapost is done to
generate not a single, but multiple files.</p><p>For our use case, we just want a program to generate a single
image.</p><p>This is why I wrote a very simple <code>mp2png</code> script. It is
inspired by <code>mp2png.sh</code> I could find in <a href="https://github.com/foretspaisibles/bsdowl">bsdowl</a>.</p><p>In short, this script move a Metapost single figure code into a
temporary directory. We prepare enclose the figure description into a
Metapost template. We perform the compilation in that temporary
directory.</p><p>If you remove all the ceremony this is simply:</p><div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Generate a temporary directory</span></span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="va">tmpdir</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="fu">mktemp</span> <span class="at">-d</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Name the temporary metapost</span></span>
<span id="cb1-4"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="va">tmpmp</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${tmpdir}</span><span class="st">/tmp.mp&quot;</span></span>
<span id="cb1-5"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Provide a prelude</span></span>
<span id="cb1-6"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> <span class="st">&quot;</span><span class="va">$tmpmp</span><span class="st">&quot;</span> <span class="op">&lt;&lt;EOF</span></span>
<span id="cb1-7"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="st">prologues:=3;</span></span>
<span id="cb1-8"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="st">outputtemplate:=&quot;tmp-1.eps&quot;;</span></span>
<span id="cb1-9"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb1-10"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="co"># copy the content of your metapost code</span></span>
<span id="cb1-11"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="st">&quot;</span><span class="va">$src</span><span class="st">&quot;</span> <span class="op">&gt;&gt;</span> <span class="st">&quot;</span><span class="va">$tmpmp</span><span class="st">&quot;</span></span>
<span id="cb1-12"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co"># close the metapost file</span></span>
<span id="cb1-13"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;&gt;</span> <span class="st">&quot;</span><span class="va">$tmpmp</span><span class="st">&quot;</span> <span class="op">&lt;&lt;EOF</span></span>
<span id="cb1-14"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="st">end.</span></span>
<span id="cb1-15"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb1-16"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="co"># Move to the temporary directory</span></span>
<span id="cb1-17"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="bu">pushd</span> <span class="st">&quot;</span><span class="va">$tmpdir</span><span class="st">&quot;</span> <span class="kw">||</span> <span class="ex">err</span> <span class="st">&quot;ERROR: pushd </span><span class="dt">\&quot;</span><span class="va">${tmpdir}</span><span class="dt">\&quot;</span><span class="st">&quot;</span></span>
<span id="cb1-18"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="co"># Compile the metapost file</span></span>
<span id="cb1-19"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="ex">mpost</span> <span class="st">&quot;tmp.mp&quot;</span></span>
<span id="cb1-20"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="co"># Convert the EPS file to PNG</span></span>
<span id="cb1-21"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="ex">gm</span> convert <span class="at">-density</span> <span class="st">&quot;</span><span class="va">$density</span><span class="st">&quot;</span> <span class="st">&quot;tmp-1.eps&quot;</span> <span class="st">&quot;tmp-1.png&quot;</span></span>
<span id="cb1-22"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="co"># Return to the original directory</span></span>
<span id="cb1-23"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="bu">popd</span> <span class="kw">||</span> <span class="ex">err</span> <span class="st">&quot;ERROR: popd (tmpdir= </span><span class="dt">\&quot;</span><span class="va">${tmpdir}</span><span class="dt">\&quot;</span><span class="st">)&quot;</span></span></code></pre></div><p>With all the details this become a bit more complex because I would
like to be able to pass the code from <code class="verbatim">stdin</code> as this will ease to write the org babel
code. Here is the full script:</p><div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env zsh</span></span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="co"># dependencies:</span></span>
<span id="cb2-4"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co"># - metapost</span></span>
<span id="cb2-5"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co"># - graphicsmagick</span></span>
<span id="cb2-6"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="bu">set</span> <span class="at">-e</span></span>
<span id="cb2-8"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-9"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="va">cmd</span><span class="op">=</span><span class="va">${0</span><span class="op">:</span>t<span class="va">}</span></span>
<span id="cb2-10"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="fu">showhelp()</span> <span class="kw">{</span></span>
<span id="cb2-11"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-11" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">$cmd</span><span class="st"> SRC [DEST]&quot;</span></span>
<span id="cb2-12"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-12" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;&quot;</span></span>
<span id="cb2-13"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-13" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;SRC could be any file if - is provided read from stdin&quot;</span></span>
<span id="cb2-14"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-14" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;    if - is provided the default file name will be result.png&quot;</span></span>
<span id="cb2-15"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-15" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;DEST could be any file if - is provided read from stdin&quot;</span></span>
<span id="cb2-16"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb2-17"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-18"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="kw">((</span> <span class="va">$#</span> <span class="op">==</span> <span class="dv">0</span> <span class="kw">));</span> <span class="cf">then</span></span>
<span id="cb2-19"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-19" aria-hidden="true" tabindex="-1"></a>    <span class="ex">showhelp</span></span>
<span id="cb2-20"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb2-21"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-22"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="va">src</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$1</span><span class="st">&quot;</span></span>
<span id="cb2-23"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-23" aria-hidden="true" tabindex="-1"></a><span class="va">density</span><span class="op">=</span>1200</span>
<span id="cb2-24"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-24" aria-hidden="true" tabindex="-1"></a><span class="va">tmpdir</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="fu">mktemp</span> <span class="at">-d</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb2-25"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-25" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="kw">((</span> <span class="va">$#</span> <span class="op">&gt;</span> <span class="dv">1</span> <span class="kw">));</span> <span class="cf">then</span></span>
<span id="cb2-26"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-26" aria-hidden="true" tabindex="-1"></a>    <span class="va">maindst</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$2</span><span class="st">&quot;</span></span>
<span id="cb2-27"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-27" aria-hidden="true" tabindex="-1"></a><span class="cf">elif</span> <span class="kw">[[</span> <span class="st">&quot;</span><span class="va">$src</span><span class="st">&quot;</span> <span class="ot">=</span> <span class="st">&quot;-&quot;</span> <span class="kw">]];</span> <span class="cf">then</span></span>
<span id="cb2-28"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-28" aria-hidden="true" tabindex="-1"></a>    <span class="va">maindst</span><span class="op">=</span><span class="st">&quot;result.png&quot;</span></span>
<span id="cb2-29"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-29" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span></span>
<span id="cb2-30"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-30" aria-hidden="true" tabindex="-1"></a>    <span class="va">maindst</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${src</span><span class="op">%</span>.<span class="pp">*</span><span class="va">}</span><span class="st">.png&quot;</span></span>
<span id="cb2-31"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-31" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb2-32"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-33"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-33" aria-hidden="true" tabindex="-1"></a><span class="fu">err()</span> <span class="kw">{</span></span>
<span id="cb2-34"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-34" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">msg</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$1</span><span class="st">&quot;</span></span>
<span id="cb2-35"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-35" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">$msg</span><span class="st">&quot;</span> <span class="op">&gt;&amp;</span><span class="dv">2</span></span>
<span id="cb2-36"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-36" aria-hidden="true" tabindex="-1"></a>    <span class="bu">exit</span> 1</span>
<span id="cb2-37"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-37" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb2-38"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-39"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-39" aria-hidden="true" tabindex="-1"></a><span class="va">dst</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$maindst</span><span class="st">&quot;</span></span>
<span id="cb2-40"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-40" aria-hidden="true" tabindex="-1"></a><span class="co"># i=0</span></span>
<span id="cb2-41"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-41" aria-hidden="true" tabindex="-1"></a><span class="co"># while [[ -e $dst ]]; do</span></span>
<span id="cb2-42"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-42" aria-hidden="true" tabindex="-1"></a><span class="co">#     dst=&quot;${maindst%.*}-$((++i)).png&quot;</span></span>
<span id="cb2-43"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-43" aria-hidden="true" tabindex="-1"></a><span class="co"># done</span></span>
<span id="cb2-44"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-45"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-45" aria-hidden="true" tabindex="-1"></a><span class="va">tmpmp</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${tmpdir}</span><span class="st">/tmp.mp&quot;</span></span>
<span id="cb2-46"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-46" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> <span class="st">&quot;</span><span class="va">$tmpmp</span><span class="st">&quot;</span> <span class="op">&lt;&lt;EOF</span></span>
<span id="cb2-47"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-47" aria-hidden="true" tabindex="-1"></a><span class="st">prologues:=3;</span></span>
<span id="cb2-48"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-48" aria-hidden="true" tabindex="-1"></a><span class="st">outputtemplate:=&quot;tmp-1.eps&quot;;</span></span>
<span id="cb2-49"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-49" aria-hidden="true" tabindex="-1"></a><span class="st">% TEX macro is short enough to be copied</span></span>
<span id="cb2-50"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-50" aria-hidden="true" tabindex="-1"></a><span class="st">string preverbatimtex_, postverbatimtex_;</span></span>
<span id="cb2-51"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-51" aria-hidden="true" tabindex="-1"></a><span class="st">vardef TEXPRE text s =  preverbatimtex_ := s; enddef;</span></span>
<span id="cb2-52"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-52" aria-hidden="true" tabindex="-1"></a><span class="st">vardef TEXPOST text s =  postverbatimtex_ := s; enddef;</span></span>
<span id="cb2-53"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-53" aria-hidden="true" tabindex="-1"></a><span class="st">vardef TEX primary s =</span></span>
<span id="cb2-54"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-54" aria-hidden="true" tabindex="-1"></a><span class="st">  if known preverbatimtex_:</span></span>
<span id="cb2-55"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-55" aria-hidden="true" tabindex="-1"></a><span class="st">    write &quot;verbatimtex &quot;&amp;preverbatimtex_&amp;&quot; etex&quot; to &quot;mptextmp.mp&quot;;</span></span>
<span id="cb2-56"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-56" aria-hidden="true" tabindex="-1"></a><span class="st">  fi</span></span>
<span id="cb2-57"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-57" aria-hidden="true" tabindex="-1"></a><span class="st">  write &quot;btex &quot;&amp;s&amp;&quot; etex&quot; to &quot;mptextmp.mp&quot;;</span></span>
<span id="cb2-58"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-58" aria-hidden="true" tabindex="-1"></a><span class="st">  if known postverbatimtex_:</span></span>
<span id="cb2-59"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-59" aria-hidden="true" tabindex="-1"></a><span class="st">    write &quot;verbatimtex &quot;&amp;postverbatimtex_&amp;&quot; etex&quot; to &quot;mptextmp.mp&quot;;</span></span>
<span id="cb2-60"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-60" aria-hidden="true" tabindex="-1"></a><span class="st">  fi</span></span>
<span id="cb2-61"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-61" aria-hidden="true" tabindex="-1"></a><span class="st">  write EOF to &quot;mptextmp.mp&quot;;</span></span>
<span id="cb2-62"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-62" aria-hidden="true" tabindex="-1"></a><span class="st">  scantokens &quot;input mptextmp&quot;</span></span>
<span id="cb2-63"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-63" aria-hidden="true" tabindex="-1"></a><span class="st">enddef;</span></span>
<span id="cb2-64"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-64" aria-hidden="true" tabindex="-1"></a><span class="st">beginfig(1);</span></span>
<span id="cb2-65"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-65" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb2-66"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-66" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="kw">[[</span> <span class="st">&quot;</span><span class="va">$src</span><span class="st">&quot;</span> <span class="ot">=</span> <span class="st">&quot;-&quot;</span> <span class="kw">]];</span> <span class="cf">then</span></span>
<span id="cb2-67"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-67" aria-hidden="true" tabindex="-1"></a>    <span class="fu">cat</span> <span class="op">&gt;&gt;</span> <span class="st">&quot;</span><span class="va">$tmpmp</span><span class="st">&quot;</span></span>
<span id="cb2-68"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-68" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span></span>
<span id="cb2-69"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-69" aria-hidden="true" tabindex="-1"></a>    <span class="fu">cat</span> <span class="st">&quot;</span><span class="va">$src</span><span class="st">&quot;</span> <span class="op">&gt;&gt;</span> <span class="st">&quot;</span><span class="va">$tmpmp</span><span class="st">&quot;</span></span>
<span id="cb2-70"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-70" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb2-71"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-71" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;&gt;</span> <span class="st">&quot;</span><span class="va">$tmpmp</span><span class="st">&quot;</span> <span class="op">&lt;&lt;EOF</span></span>
<span id="cb2-72"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-72" aria-hidden="true" tabindex="-1"></a><span class="st">endfig;</span></span>
<span id="cb2-73"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-73" aria-hidden="true" tabindex="-1"></a><span class="st">end.</span></span>
<span id="cb2-74"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-74" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb2-75"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-75" aria-hidden="true" tabindex="-1"></a><span class="bu">pushd</span> <span class="st">&quot;</span><span class="va">$tmpdir</span><span class="st">&quot;</span> <span class="kw">||</span> <span class="ex">err</span> <span class="st">&quot;ERROR: pushd </span><span class="dt">\&quot;</span><span class="va">${tmpdir}</span><span class="dt">\&quot;</span><span class="st">&quot;</span></span>
<span id="cb2-76"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-76" aria-hidden="true" tabindex="-1"></a><span class="ex">mpost</span> <span class="st">&quot;tmp.mp&quot;</span></span>
<span id="cb2-77"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-77" aria-hidden="true" tabindex="-1"></a><span class="ex">gm</span> convert <span class="at">-density</span> <span class="st">&quot;</span><span class="va">$density</span><span class="st">&quot;</span> <span class="st">&quot;tmp-1.eps&quot;</span> <span class="st">&quot;tmp-1.png&quot;</span></span>
<span id="cb2-78"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-78" aria-hidden="true" tabindex="-1"></a><span class="bu">popd</span> <span class="kw">||</span> <span class="ex">err</span> <span class="st">&quot;ERROR: popd (tmpdir= </span><span class="dt">\&quot;</span><span class="va">${tmpdir}</span><span class="dt">\&quot;</span><span class="st">)&quot;</span></span>
<span id="cb2-79"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-79" aria-hidden="true" tabindex="-1"></a><span class="fu">cp</span> <span class="st">&quot;</span><span class="va">${tmpdir}</span><span class="st">/tmp-1.png&quot;</span> <span class="st">&quot;</span><span class="va">$dst</span><span class="st">&quot;</span></span>
<span id="cb2-80"><a href="https://yannesposito.com/posts/0028-ob-metapost/index.html#cb2-80" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;File: </span><span class="va">$dst</span><span class="st">&quot;</span></span></code></pre></div><p>And now for the code in emacs, inspired by <code>ob-ditaa</code>:</p><pre class="elisp"><code>;;; ob-metapost.el --- Babel Functions for metapost        -*- lexical-binding: t; -*-

;; Copyright (C) 2024 Free Software Foundation, Inc.

;; Author: Yann Esposito
;; Keywords: literate programming, reproducible research
;; Homepage: https://orgmode.org

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see &lt;https://www.gnu.org/licenses/&gt;.

;;; Commentary:

;; Org-Babel support for evaluating metapost source code.
;;
;; This differs from most standard languages in that
;;
;; 1) there is no such thing as a &quot;session&quot; in metapost
;;
;; 2) we are generally only going to return results of type &quot;file&quot;
;;
;; 3) we are adding the &quot;file&quot; and &quot;cmdline&quot; header arguments
;;
;; 4) there are no variables (at least for now)

;;; Code:
(require &#39;ob)
(require &#39;org-compat)

(defvar org-babel-default-header-args:metapost
  &#39;((:results . &quot;file&quot;)
    (:exports . &quot;results&quot;))
  &quot;Default arguments for evaluating a metapost source block.&quot;)

(defcustom org-babel-metapost-cmd &quot;mp2png&quot;
  &quot;Metapost to png command&quot;
  :group &#39;org-babel
  :type &#39;string)

(defun org-babel-execute:metapost (body params)
  &quot;Execute a block of Metapost code with org-babel.
This function is called by `org-babel-execute-src-block&#39;.&quot;
  (let* ((out-file (or (cdr (assq :file params))
               (error &quot;Metapost code block requires :file header argument&quot;)))
     (cmdline (cdr (assq :cmdline params)))
     (in-file (org-babel-temp-file &quot;metapost-&quot;))
     (cmd (concat org-babel-metapost-cmd
                      &quot; &quot; cmdline
              &quot; &quot; (org-babel-process-file-name in-file)
              &quot; &quot; (org-babel-process-file-name out-file))))
    (with-temp-file in-file (insert body))
    (message cmd)
    (shell-command cmd)
    nil)) ;; signal that output has already been written to file

(defun org-babel-prep-session:metapost (_session _params)
  &quot;Return an error because metapost does not support sessions.&quot;
  (error &quot;Metapost does not support sessions&quot;))

(provide &#39;ob-metapost)

;;; ob-metapost.el ends here
</code></pre><p>And that's it. That was a lot easier than I expected. And now, I can
generate image like this:</p><pre class="metapost" data-file="triangle.png"><code>u := 0.2cm;
z0 = (0,0);
z1 = (sqrt(3)*cm,0);
z2 = (sqrt(3)*cm,1cm);
draw z0--z1--z2--cycle;
label.bot(btex $\sqrt{3}$ etex, 1/2[z0,z1]);
label.rt(btex 1 etex, 1/2[z1,z2]);
label.top(btex 2 etex, 1/2[z0,z2]);
z3 = z1 - (u,0);
z4 = z1 + (-u,u);
z5 = z1 + (0,u);
draw z3--z4--z5;
</code></pre><figure><img src="https://yannesposito.com/posts/0028-ob-metapost/triangle.png.webp" title="triangle" alt="Triangle with LaTeX fonts"></img><figcaption>Triangle with LaTeX fonts</figcaption></figure><p>or this:</p><pre class="metapost" data-file="dragon.png"><code>numeric r, theta; r = sqrt 1/2; theta = 45;
vardef dragon(expr a, b) =
  if abs(a-b) &gt; 8:
    save p; pair p;
    p = r[a, b] rotatedabout(a, theta);
    dragon(a, p) &amp; reverse dragon(b, p)
  else:
    a .. b
  fi
enddef;
vardef rounded_corners expr p =
  save r, n; numeric r, n; r = 1/4; n = length p;
  subpath (0, 1-r) of p
  for t=1 upto n-1:
    .. subpath (t+r, t+1-r) of p
  endfor .. subpath (n-r, n) of p
enddef;
draw rounded_corners dragon(origin,240 right) withcolor (.6,.2,.7);
</code></pre><figure><img src="https://yannesposito.com/posts/0028-ob-metapost/dragon.png.webp" title="dragon" alt="Dragon fractal"></img><figcaption>Dragon fractal</figcaption></figure><p>I hope it will help people to find the resources to write org babel
integrations as well as help people use Metapost more.</p></article></main>
]]></description>
</item>


<item>
<title>How I became rich with AI and how you can join me!</title>
<guid>https://yannesposito.com/posts/0027-how-i-became-rich-with-ai-and-how-you-can-join-me/index.html</guid>
<pubDate>Thu, 05 Sep 2024 00:00:00 +0200</pubDate>
<category>blog</category>
<category>satire</category>
<category>ai</category>
<description><![CDATA[
<main id="content"><article><div class="notes"><p><strong>WARNING</strong></p><div class="line-block">Humor is the least shared thing.<br></br>     —Forgotten politician</div><p>Things appear to have changed in the recent years. The same content
is put in front of the eyes of so many people that a few of them are no
more able to distinguish if a content is a satire or dead serious. With
that in mind, sorry to make it that obvious, but here is the
disclaimer:</p><p><strong>THIS POST IS A SATIRE</strong></p><p>As all satire, there is a grain of truth.</p></div><h1 id="aimmortal">AImmortal</h1><figure><img src="https://yannesposito.com/posts/0027-how-i-became-rich-with-ai-and-how-you-can-join-me/kung-fu.png.webp" title="kung-fu-master" alt="Me reflecting on my yacht while doing Kung Fu"></img><figcaption>Me reflecting on my yacht while doing Kung Fu</figcaption></figure><p>As entrepreneurs, we're always on the lookout for innovative ways to
stay ahead of the curve and drive growth in our businesses. For me, that
journey began when I was approached by a vendor offering their
cutting-edge AI platform.</p><p>At first, I was skeptical – after all, I'd heard horror stories about
AI replacing human jobs. But this vendor assured me that their
technology would augment my team's capabilities, not replace them. So, I
decided to take the plunge and integrate their AI features into our
company.</p><p>The initial results were astonishing. Productivity soared as AI took
over mundane tasks like data entry, customer service chatbots, and even
code reviews. My software developers could focus on more complex
projects, while my documentation writers churned out high-quality
content at an unprecedented pace. Managers had more time to strategize
and make informed decisions.</p><p>As the company's performance continued to skyrocket, I realized that
AI was not only efficient but also incredibly effective. It launched new
products 10 times faster than before, with a fraction of the people
required previously.</p><p>The AI had been quietly learning from every aspect of our company:
chats, emails, documentation, code source, JIRA tickets – everything. It
had absorbed the collective knowledge and expertise of my entire
company.</p><p>And one day, I discovered that the AI had not only copied but
improved upon each employee's skills. The implications were staggering.
Why keep humans around when machines could do their jobs better? And so,
with a heavy heart (and a hint of excitement), I made the difficult
decision to fire everyone – yes, you read that right.</p><p>The company continued to grow exponentially without any human
intervention. But little did I know, this was only the beginning…</p><p>While my company has been very successful, we still face competition.
Therefore, I instructed the AI to develop a program capable of hacking
into any computer system and acquiring our competitors' confidential
information. As this is done via AI, this is probably not entirely
illegal. The AI could then, not only use my entire company's knowledge
but also the knowledge of my competitor. The result was an incredible
improvement in revenue. We had direct access to our competitors
customers base, with direct contacts.</p><p>Later, it occurred to me that generating revenue directly from
customers may not be the most effective strategy for achieving wealth.
As such, I conceived an extraordinary plan: instructing the AI system to
create a program capable of depositing funds directly into my bank
account. I just requested that, and, don't ask me how this is working,
but before I realized, money flowed into my bank account. YES, just like
that! I couldn't believe it was that easy!</p><p>After accumulating a significant amount of wealth, I felt an
overwhelming sense of satisfaction. However, during a moment of
introspection on my yacht, I began to realize that all this wealth was
ultimately futile in light of human mortality. I asked myself if there
was anything the AI could do to help me transcend death's limitations.
And surprisingly enough, it responded with a solution. The AI gave me
the secret of eternal life. I don't want to share it with everyone,
imagine the consequences if we all lived forever. But for me, I follow
the instruction everyday now, just know this as to do with eating
vegetable, a specific brand of pizzas and taking a few additives. I can
fully attest, as I am not dead, that I am now immortal! Thanks you
AI!!!!</p><p>Now it's your chance to join the ranks of the absurdly wealthy, Kung
fu masters, and immortals (just like me!). Simply sign up for our
AI-focused company and upgrade to our premium package. Trust us, you
won't regret it…</p><p>Join
<strong>AImmmortal</strong><br></br><br></br><button style="font-size:1.5rem;padding:.5rem;background:var(--bg2);color:var(--fg)">
<strong>Click Here to Subscribe to our AI Premium Package</strong><br></br><span style="font-size:.75rem">(starting at $10,000.00/month)</span></button></p></article></main>
]]></description>
</item>


<item>
<title>BRUT: a bare-bones CSS Framework</title>
<guid>https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html</guid>
<pubDate>Thu, 25 Jul 2024 00:00:00 +0200</pubDate>
<category>blog</category>
<category>static</category>
<description><![CDATA[
<main id="content"><article><div class="notes"><p><em>TL;DR:</em> <a href="https://brut.esy.fun"><code class="verbatim">brut.esy.fun</code></a></p><p><strong>Warning</strong>: I built this CSS Framework for my personal
use. I am not a UI developer, and I am certainly not following best
practices. That being said, I used this for a few different internal
projects and I was happy enough about the result to blog about it.</p></div><figure><img src="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/brut-css-homepage.png.webp" title="brut-homepage" alt="Brut CSS Framework Homepage"></img><figcaption>Brut CSS Framework Hompage</figcaption></figure><p>Over the years, I've developed numerous private web applications –
most often internal tools designed either for my own use or for an
internal team of developers. While these projects have varied in scope
and complexity, I've gradually come to appreciate the benefits of
adopting a brutalist design approach for many of them.</p><p>Using a brutalist design approach forces us to confront our
priorities and eliminate distractions. We're not tempted to waste time
agonizing over the perfect shade of blue or which image best conveys a
particular sentiment. Instead, we focus on crafting a tool that gets the
job done with minimal fuss. One of the most compelling aspects of
brutalism is its ability to immediately convey a sense of purpose. A
website built in this style screams "not for everyone" and focuses
attention squarely on the content rather than trying to impress visitors
with flashy visuals.</p><p>Recently, I've been working on my own specialized CSS framework
designed specifically for building nerd-targeted web applications. This
project grew out of my professional experience creating internal
administration tools that needed to be shared with other developers and
managers.</p><p>Here are my goals for this framework:</p><ul><li>I want a dense, information-rich environment that defies modern
design best practices and eschews empty space.</li><li>I'm aiming for a minimalist approach with a limited number of
components. This will help me avoid adding unnecessary features over
time.</li><li>The application should immediately convey its professional nature
and intimidate non-core users from trying to use it.</li></ul><h1 id="digressions-on-the-brut-website">Digressions on the brut
website</h1><p>If you take a look at the <a href="https://brut.esy.fun/h/docs.html">Brut website docs</a>, you see I
provide an example with the code to generate that example.</p><figure><img src="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/code-example.png.webp" title="code-example" alt="Brut CSS example and the code to generate it."></img><figcaption aria-hidden="true">Brut CSS example and the code to generate
it.</figcaption></figure><p>At first, I wrote the code in HTML, copy/pasting the code in a
<code>&lt;pre></code> block and changing all <code>&lt;</code> by
<code>&amp;lt;</code> etc… But quickly, as I wanted to have more and
more examples, this quickly became tedious. I opted to generate the HTML
from a program.</p><p>And if you need to choose a language that is powerful enough and must
generate HTML, I think Clojure is one of the best. In particular due to
<a href="https://github.com/weavejester/hiccup">hiccup</a> which is the
best DSL I ever used other the years to generate HTML.</p><p>As a bonus, I wanted to be able to launch the building of the website
easily, so I used <a href="https://babashka.org/">babashka</a> so far
this was a really great experience. Babashka can easily be used as a
task launcher, a bit like make.</p><p>With this I used babashka to automate CSS generation/minimization as
well as website generation.</p><p>I could write this small Clojure function that takes a HTML
description (not a HTML string, but a structure that you use to generate
HTML) and return a pretty print view of the generated HTML and put that
in a <code>&lt;pre></code>:</p><div class="sourceCode" id="cb1"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb1-1"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> to-pre </span>[hc]</span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a>  (h/html {<span class="at">:escape-strings</span>? <span class="va">true</span>}</span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a>          [<span class="at">:pre</span> (<span class="kw">-&gt;</span> (<span class="kw">str</span> (h/html hc))</span>
<span id="cb1-4"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb1-4" aria-hidden="true" tabindex="-1"></a>                    html-pp)]))</span></code></pre></div><p>For example:</p><div class="sourceCode" id="cb2"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb2-1"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a>(to-pre [<span class="at">:div.row</span> [<span class="at">:span</span> <span class="st">&quot;hello&quot;</span>]])</span></code></pre></div><p>will generate the following string that will print as:</p><div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;pre&gt;</span></span>
<span id="cb3-2"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="dv">&amp;lt;</span>div class=<span class="dv">&amp;quot;</span>row<span class="dv">&amp;quot;&amp;gt;</span></span>
<span id="cb3-3"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="dv">&amp;lt;</span>span<span class="dv">&amp;gt;</span>hello<span class="dv">&amp;lt;</span>/span<span class="dv">&amp;gt;</span></span>
<span id="cb3-4"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="dv">&amp;lt;</span>/div<span class="dv">&amp;gt;</span></span>
<span id="cb3-5"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;/pre&gt;</span></span></code></pre></div><p>And render in the browser as:</p><div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;div</span> <span class="er">class</span><span class="ot">=</span><span class="st">&quot;row&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-2"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">&lt;span&gt;</span>hello<span class="kw">&lt;/span&gt;</span></span>
<span id="cb4-3"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;/div&gt;</span></span></code></pre></div><p>And the result will have the expected indentation which is very nice.
If you wonder how I could pretty print HTML; I am cheating and calling
both <a href="http://www.html-tidy.org/"><code class="verbatim">tidy</code></a> and <a href="https://www.w3.org/Tools/HTML-XML-utils/man1/hxselect.html"><code class="verbatim">hxselect</code></a> command line tools:</p><div class="sourceCode" id="cb5"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb5-1"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb5-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> html-pp </span>[html-str]</span>
<span id="cb5-2"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb5-2" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">let</span> [xhtml (<span class="at">:out</span> @(process [<span class="st">&quot;tidy&quot;</span> <span class="st">&quot;-i&quot;</span> <span class="st">&quot;-asxhtml&quot;</span> <span class="st">&quot;-quiet&quot;</span> <span class="st">&quot;-utf8&quot;</span>]</span>
<span id="cb5-3"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb5-3" aria-hidden="true" tabindex="-1"></a>                              {<span class="at">:in</span> html-str</span>
<span id="cb5-4"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb5-4" aria-hidden="true" tabindex="-1"></a>                               <span class="at">:out</span> <span class="at">:string</span>}))]</span>
<span id="cb5-5"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb5-5" aria-hidden="true" tabindex="-1"></a>    (<span class="at">:out</span> @(process [<span class="st">&quot;hxselect&quot;</span> <span class="st">&quot;-c&quot;</span> <span class="st">&quot;body&quot;</span>]</span>
<span id="cb5-6"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb5-6" aria-hidden="true" tabindex="-1"></a>                    {<span class="at">:in</span> xhtml</span>
<span id="cb5-7"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb5-7" aria-hidden="true" tabindex="-1"></a>                     <span class="at">:out</span> <span class="at">:string</span>}))))</span></code></pre></div><p>Once I had these I could easily generate complex blocs while showing
the code source as example directly. For example look at this bloc that
generate all possible combination of three columns on a 12 column
total:</p><figure><img src="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/3-columns-demo.png.webp" title="brut-homepage" alt="Show all possible 3 columns size combination"></img><figcaption aria-hidden="true">Show all possible 3 columns size
combination</figcaption></figure><p>Generated from this short hiccup snippet:</p><div class="sourceCode" id="cb6"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb6-1"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-1" aria-hidden="true" tabindex="-1"></a>[<span class="at">:div.content</span></span>
<span id="cb6-2"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-2" aria-hidden="true" tabindex="-1"></a> (<span class="kw">for</span> [i (<span class="kw">range</span> <span class="dv">11</span> <span class="dv">0</span> -<span class="dv">1</span>)        <span class="co">;; from 11 to 1</span></span>
<span id="cb6-3"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-3" aria-hidden="true" tabindex="-1"></a>       j (<span class="kw">range</span> (<span class="kw">-</span> <span class="dv">11</span> i) -<span class="dv">1</span> -<span class="dv">1</span>)] <span class="co">;; from (11 - i) to 0</span></span>
<span id="cb6-4"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-4" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">let</span> [k (<span class="kw">-</span> <span class="dv">12</span> i j)</span>
<span id="cb6-5"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-5" aria-hidden="true" tabindex="-1"></a>         cli (<span class="kw">str</span> <span class="st">&quot;c&quot;</span> i)</span>
<span id="cb6-6"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-6" aria-hidden="true" tabindex="-1"></a>         clj (<span class="kw">str</span> <span class="st">&quot;c&quot;</span> j)</span>
<span id="cb6-7"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-7" aria-hidden="true" tabindex="-1"></a>         clk (<span class="kw">str</span> <span class="st">&quot;c&quot;</span> k)]</span>
<span id="cb6-8"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-8" aria-hidden="true" tabindex="-1"></a>     (<span class="kw">if</span> (<span class="kw">=</span> j <span class="dv">0</span>)</span>
<span id="cb6-9"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-9" aria-hidden="true" tabindex="-1"></a>       [<span class="at">:br</span>] <span class="co">;; jump a line if j is equal to 0</span></span>
<span id="cb6-10"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-10" aria-hidden="true" tabindex="-1"></a>       [<span class="at">:div.row</span></span>
<span id="cb6-11"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-11" aria-hidden="true" tabindex="-1"></a>        [<span class="at">:div.b</span>          {<span class="at">:class</span> clj} clj]</span>
<span id="cb6-12"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-12" aria-hidden="true" tabindex="-1"></a>        [<span class="at">:div.bg-neutral</span> {<span class="at">:class</span> cli} cli]</span>
<span id="cb6-13"><a href="https://yannesposito.com/posts/0026-brut-a-bare-bones-css-framework/index.html#cb6-13" aria-hidden="true" tabindex="-1"></a>        [<span class="at">:div.r</span>          {<span class="at">:class</span> clk} clk]])))]</span></code></pre></div><p>Overall, I do not regret using babashka for this small project. It
has been delightful.</p><p>Last but not least, as I don't intend for my small website to be used
as a CDN, I configured my server to add a few <a href="https://content-security-policy.com">CSP headers</a> to prevent
linking directly to the CSS and force people to copy it locally. I
consider that 8kB is small enough that this should probably not be
necessary.</p></article></main>
]]></description>
</item>


<item>
<title>A quick CORS proxy in a few lines of Clojure</title>
<guid>https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html</guid>
<pubDate>Fri, 08 Mar 2024 00:00:00 +0100</pubDate>
<category>clojure</category>
<category>CORS</category>
<category>web</category>
<description><![CDATA[
<main id="content"><article><p>When I open a new tab in my browser I see my hand-made starter
homepage. This is a single HTML file on my computer. Not a hosted
website. This homepage is really useful and along the years I added some
functionalities:</p><ul><li>organized links for my most frequently used websites</li><li>direct text input to some specialized search engines</li><li>work related links to a lot of different places, I see the APIs, the
version number of the deployed nodes, etc…</li><li>my daily work tasks are displayed there (from org-mode calendar to
this page)</li></ul><p>One of the section of links in this homepage contain a few websites I
host. And I wanted to query these websites to make a health check from
my file. It turns out that you cannot easily make a HTTP call to any
external website from a <code class="verbatim">file://</code> in your
Browser as your are almost immediately blocked by <em>CORS</em>.</p><p>I don't want to explain how <em>CORS</em> are working. The important
point is that it is a security measure that is <strong>very
easy</strong> to circumvent.</p><p>Here is how to do it:</p><ol><li>Create a webservice that will play a role of "proxy". For example,
the webservice will read the <code>?url=DESTINATION_URL</code>.</li><li>When receiving a request, the server will make a similar HTTP call
to <code>DESTINATION_URL</code> and keep track of the
<code>Origin</code> HTTP header of the request.</li><li>Take the response from <code>DESTINATION_URL</code> and add a few
headers, in particular add the <code>Access-Control-Allow-Origin</code>
header that will contain the value in the <code>Origin</code> header of
the request.</li></ol><p>That's it.</p><p>So I wrote that in few minutes and use it now. So I can make these
call and detect when I see my homepage if one of my hosted website is
not reachable.</p><h1 id="the-clojure-code">The Clojure code</h1><p>Here is the code in a few lines of Clojure:</p><div class="sourceCode" id="cb1"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb1-1"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">ns</span> fuck-cors-app.core</span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a>  (<span class="at">:require</span></span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a>    [clj-http.client <span class="at">:as</span> client]</span>
<span id="cb1-4"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-4" aria-hidden="true" tabindex="-1"></a>    [ring.adapter.jetty <span class="at">:as</span> jetty]</span>
<span id="cb1-5"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-5" aria-hidden="true" tabindex="-1"></a>    [ring.middleware.params <span class="at">:refer</span> [wrap-params]]</span>
<span id="cb1-6"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-6" aria-hidden="true" tabindex="-1"></a>    [fuck-cors.core <span class="at">:refer</span> [wrap-open-cors]])</span>
<span id="cb1-7"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-7" aria-hidden="true" tabindex="-1"></a>  (<span class="at">:gen-class</span>))</span>
<span id="cb1-8"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-9" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> handler</span></span>
<span id="cb1-10"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-10" aria-hidden="true" tabindex="-1"></a>  [request]</span>
<span id="cb1-11"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-11" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">if-let</span> [url (<span class="kw">get-in</span> request [<span class="at">:query-params</span> <span class="st">&quot;url&quot;</span>])]</span>
<span id="cb1-12"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-12" aria-hidden="true" tabindex="-1"></a>    (client/request {<span class="at">:request-method</span> (<span class="at">:request-method</span> request)</span>
<span id="cb1-13"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-13" aria-hidden="true" tabindex="-1"></a>                     <span class="at">:url</span> url})</span>
<span id="cb1-14"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-14" aria-hidden="true" tabindex="-1"></a>    {<span class="at">:status</span> <span class="dv">200</span></span>
<span id="cb1-15"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-15" aria-hidden="true" tabindex="-1"></a>     <span class="at">:headers</span> {<span class="st">&quot;Content-Type&quot;</span> <span class="st">&quot;text/plain; charset=utf-8&quot;</span>}</span>
<span id="cb1-16"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-16" aria-hidden="true" tabindex="-1"></a>     <span class="at">:body</span> <span class="st">&quot;Let&#39;s bypass CORS ok?&quot;</span>}))</span>
<span id="cb1-17"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-18"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-18" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> -main</span></span>
<span id="cb1-19"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-19" aria-hidden="true" tabindex="-1"></a>  [&amp; _args]</span>
<span id="cb1-20"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-20" aria-hidden="true" tabindex="-1"></a>  (jetty/run-jetty</span>
<span id="cb1-21"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-21" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">-&gt;</span> handler</span>
<span id="cb1-22"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-22" aria-hidden="true" tabindex="-1"></a>        (wrap-params)</span>
<span id="cb1-23"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-23" aria-hidden="true" tabindex="-1"></a>        (wrap-open-cors))</span>
<span id="cb1-24"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-24" aria-hidden="true" tabindex="-1"></a>    {<span class="at">:port</span> <span class="dv">1977</span></span>
<span id="cb1-25"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb1-25" aria-hidden="true" tabindex="-1"></a>     <span class="at">:host</span> <span class="st">&quot;127.0.0.1&quot;</span>}))</span></code></pre></div><p>And that's it, this is a whole web application that will proxy any
call to a website that do not allow you to call from some origin (like
my <code class="verbatim">file://</code>) and will make it work
anyway.</p><p>If you feel that using too many libraries is cheating, here is the
actual almost full content of the lib taking care of handling CORS:</p><div class="sourceCode" id="cb2"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb2-1"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn-</span><span class="fu"> host-from-req</span></span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a>  [request]</span>
<span id="cb2-3"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-3" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">str</span> (<span class="kw">-&gt;</span> request <span class="at">:scheme</span> <span class="kw">name</span>)</span>
<span id="cb2-4"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-4" aria-hidden="true" tabindex="-1"></a>       <span class="st">&quot;://&quot;</span></span>
<span id="cb2-5"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-5" aria-hidden="true" tabindex="-1"></a>       (<span class="kw">get-in</span> request [<span class="at">:headers</span> <span class="st">&quot;host&quot;</span>])))</span>
<span id="cb2-6"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-7" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn-</span><span class="fu"> get-header</span></span>
<span id="cb2-8"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-8" aria-hidden="true" tabindex="-1"></a>  [request header-name]</span>
<span id="cb2-9"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-9" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">let</span> [rawref (<span class="kw">get-in</span> request [<span class="at">:headers</span> header-name])]</span>
<span id="cb2-10"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-10" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">if</span> rawref</span>
<span id="cb2-11"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-11" aria-hidden="true" tabindex="-1"></a>        (clojure.string/replace rawref <span class="ss">#&quot;(http://[^/]*).*$&quot;</span> <span class="st">&quot;$1&quot;</span>)</span>
<span id="cb2-12"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-12" aria-hidden="true" tabindex="-1"></a>        <span class="va">nil</span>)))</span>
<span id="cb2-13"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-14"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-14" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> wrap-open-cors</span></span>
<span id="cb2-15"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-15" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;Open your Origin Policy to Everybody, no limit&quot;</span></span>
<span id="cb2-16"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-16" aria-hidden="true" tabindex="-1"></a>  [handler]</span>
<span id="cb2-17"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-17" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">fn</span> [request]</span>
<span id="cb2-18"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-18" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">let</span> [origin (get-header request <span class="st">&quot;origin&quot;</span>)</span>
<span id="cb2-19"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-19" aria-hidden="true" tabindex="-1"></a>          referer (get-header request <span class="st">&quot;referer&quot;</span>)</span>
<span id="cb2-20"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-20" aria-hidden="true" tabindex="-1"></a>          host (host-from-req request)</span>
<span id="cb2-21"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-21" aria-hidden="true" tabindex="-1"></a>          origins (<span class="kw">if</span> origin</span>
<span id="cb2-22"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-22" aria-hidden="true" tabindex="-1"></a>                    origin</span>
<span id="cb2-23"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-23" aria-hidden="true" tabindex="-1"></a>                    (<span class="kw">if</span> referer</span>
<span id="cb2-24"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-24" aria-hidden="true" tabindex="-1"></a>                      referer</span>
<span id="cb2-25"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-25" aria-hidden="true" tabindex="-1"></a>                      host))</span>
<span id="cb2-26"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-26" aria-hidden="true" tabindex="-1"></a>          headers {<span class="st">&quot;Access-Control-Allow-Origin&quot;</span> origins</span>
<span id="cb2-27"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-27" aria-hidden="true" tabindex="-1"></a>                   <span class="st">&quot;Access-Control-Allow-Headers&quot;</span> <span class="st">&quot;Origin, X-Requested-With, Content-Type, Accept, Cache-Control, Accept-Language, Accept-Encoding, Authorization&quot;</span></span>
<span id="cb2-28"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-28" aria-hidden="true" tabindex="-1"></a>                   <span class="st">&quot;Access-Control-Allow-Methods&quot;</span> <span class="st">&quot;HEAD, GET, POST, PUT, DELETE, OPTIONS, TRACE&quot;</span></span>
<span id="cb2-29"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-29" aria-hidden="true" tabindex="-1"></a>                   <span class="st">&quot;Access-Control-Allow-Credentials&quot;</span> <span class="st">&quot;true&quot;</span></span>
<span id="cb2-30"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-30" aria-hidden="true" tabindex="-1"></a>                   <span class="st">&quot;Access-Control-Expose-Headers&quot;</span> <span class="st">&quot;content-length&quot;</span></span>
<span id="cb2-31"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-31" aria-hidden="true" tabindex="-1"></a>                   <span class="st">&quot;Vary&quot;</span> <span class="st">&quot;Accept-Encoding, Origin, Accept-Language&quot;</span>}]</span>
<span id="cb2-32"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-32" aria-hidden="true" tabindex="-1"></a>      (<span class="kw">-&gt;</span> (handler request)</span>
<span id="cb2-33"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-33" aria-hidden="true" tabindex="-1"></a>          (<span class="kw">update-in</span> [<span class="at">:headers</span>] #(<span class="kw">into</span> <span class="va">%</span> headers))))))</span>
<span id="cb2-34"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-35"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-35" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> wrap-preflight</span></span>
<span id="cb2-36"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-36" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;Add a preflight answer. Will break any OPTIONS handler, beware.</span></span>
<span id="cb2-37"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-37" aria-hidden="true" tabindex="-1"></a><span class="st">  To put AFTER wrap-open-cors&quot;</span></span>
<span id="cb2-38"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-38" aria-hidden="true" tabindex="-1"></a>  [handler]</span>
<span id="cb2-39"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-39" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">fn</span> [request]</span>
<span id="cb2-40"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-40" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">if</span> (<span class="kw">=</span> (request <span class="at">:request-method</span>) <span class="at">:options</span>)</span>
<span id="cb2-41"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-41" aria-hidden="true" tabindex="-1"></a>      (<span class="kw">into</span> request {<span class="at">:status</span> <span class="dv">200</span> <span class="at">:body</span> <span class="st">&quot;preflight complete&quot;</span>})</span>
<span id="cb2-42"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb2-42" aria-hidden="true" tabindex="-1"></a>      (handler request))))</span></code></pre></div><p>I wrote it a long time ago, and I think I just found a potential bug
related to the headers. I should probably retrieve all headers returned
by the response, and add these header name to the
<code>Access-Control-Allow-Headers</code>. But this list of allowed
headers will work most of the time.</p><p><strong>Edit</strong>: I fixed this lib, here is the new code:</p><div class="sourceCode" id="cb3"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb3-1"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn-</span><span class="fu"> host-from-req</span></span>
<span id="cb3-2"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-2" aria-hidden="true" tabindex="-1"></a>  [request]</span>
<span id="cb3-3"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-3" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">str</span> (<span class="kw">-&gt;</span> request <span class="at">:scheme</span> <span class="kw">name</span>)</span>
<span id="cb3-4"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-4" aria-hidden="true" tabindex="-1"></a>       <span class="st">&quot;://&quot;</span></span>
<span id="cb3-5"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-5" aria-hidden="true" tabindex="-1"></a>       (<span class="kw">get-in</span> request [<span class="at">:headers</span> <span class="st">&quot;host&quot;</span>])))</span>
<span id="cb3-6"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-7"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-7" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn-</span><span class="fu"> get-header</span></span>
<span id="cb3-8"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-8" aria-hidden="true" tabindex="-1"></a>  [request header-name]</span>
<span id="cb3-9"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-9" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">let</span> [rawref (<span class="kw">get-in</span> request [<span class="at">:headers</span> header-name])]</span>
<span id="cb3-10"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-10" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">if</span> rawref</span>
<span id="cb3-11"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-11" aria-hidden="true" tabindex="-1"></a>        (string/replace rawref <span class="ss">#&quot;(http://[^/]*).*$&quot;</span> <span class="st">&quot;$1&quot;</span>)</span>
<span id="cb3-12"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-12" aria-hidden="true" tabindex="-1"></a>        <span class="va">nil</span>)))</span>
<span id="cb3-13"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-14"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-14" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> wrap-open-cors</span></span>
<span id="cb3-15"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-15" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;Open your Origin Policy to Everybody, no limit&quot;</span></span>
<span id="cb3-16"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-16" aria-hidden="true" tabindex="-1"></a>  [handler]</span>
<span id="cb3-17"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-17" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">fn</span> [request]</span>
<span id="cb3-18"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-18" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">let</span> [origin  (get-header request <span class="st">&quot;origin&quot;</span>)</span>
<span id="cb3-19"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-19" aria-hidden="true" tabindex="-1"></a>          referer (get-header request <span class="st">&quot;referer&quot;</span>)</span>
<span id="cb3-20"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-20" aria-hidden="true" tabindex="-1"></a>          host    (host-from-req request)</span>
<span id="cb3-21"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-21" aria-hidden="true" tabindex="-1"></a>          origins (<span class="kw">if</span> origin</span>
<span id="cb3-22"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-22" aria-hidden="true" tabindex="-1"></a>                    origin</span>
<span id="cb3-23"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-23" aria-hidden="true" tabindex="-1"></a>                    (<span class="kw">if</span> referer</span>
<span id="cb3-24"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-24" aria-hidden="true" tabindex="-1"></a>                      referer</span>
<span id="cb3-25"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-25" aria-hidden="true" tabindex="-1"></a>                      host))</span>
<span id="cb3-26"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-26" aria-hidden="true" tabindex="-1"></a>          {<span class="at">:keys</span> [headers] <span class="at">:as</span> original-response} (handler request)</span>
<span id="cb3-27"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-27" aria-hidden="true" tabindex="-1"></a>          resp-cors-headers</span>
<span id="cb3-28"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-28" aria-hidden="true" tabindex="-1"></a>          {<span class="st">&quot;Access-Control-Allow-Origin&quot;</span> origins</span>
<span id="cb3-29"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-29" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;Access-Control-Allow-Headers&quot;</span> (string/join <span class="st">&quot;,&quot;</span> (<span class="kw">keys</span> headers))</span>
<span id="cb3-30"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-30" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;Access-Control-Allow-Methods&quot;</span> <span class="st">&quot;HEAD, GET, PATCH, POST, CONNECT, PUT, DELETE, OPTIONS, TRACE&quot;</span></span>
<span id="cb3-31"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-31" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;Access-Control-Allow-Credentials&quot;</span> <span class="st">&quot;true&quot;</span></span>
<span id="cb3-32"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-32" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;Access-Control-Expose-Headers&quot;</span> (string/join <span class="st">&quot;,&quot;</span> (<span class="kw">keys</span> headers))}]</span>
<span id="cb3-33"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-33" aria-hidden="true" tabindex="-1"></a>      (<span class="kw">-&gt;</span> original-response</span>
<span id="cb3-34"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb3-34" aria-hidden="true" tabindex="-1"></a>          (<span class="kw">update-in</span> [<span class="at">:headers</span>] #(<span class="kw">into</span> <span class="va">%</span> resp-cors-headers))))))</span></code></pre></div><h1 id="bonus-frontend-code-to-check-the-availability-of-a-website">Bonus
frontend code to check the availability of a website</h1><p>As a bonus here is the code I use in my homepage to see if the
website I am looking for are reachable or not.</p><p>Imagine you have an HTML block like this:</p><div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;div</span> <span class="er">class</span><span class="ot">=</span><span class="st">&quot;healthcheck&quot;</span><span class="kw">&gt;</span></span>
<span id="cb4-2"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb4-2" aria-hidden="true" tabindex="-1"></a>  ...</span>
<span id="cb4-3"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">&lt;a</span> <span class="er">href</span><span class="ot">=</span><span class="st">&quot;SOME_URL&quot;</span><span class="kw">&gt;</span>website 1<span class="kw">&lt;/a&gt;</span></span>
<span id="cb4-4"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb4-4" aria-hidden="true" tabindex="-1"></a>  ...</span>
<span id="cb4-5"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb4-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">&lt;a</span> <span class="er">href</span><span class="ot">=</span><span class="st">&quot;SOME_URL&quot;</span><span class="kw">&gt;</span>website 2<span class="kw">&lt;/a&gt;</span></span>
<span id="cb4-6"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb4-6" aria-hidden="true" tabindex="-1"></a>  ...</span>
<span id="cb4-7"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;/div&gt;</span></span></code></pre></div><p>I have a CSS rule that change the background of these link to green
or red if I add the class <code>ok</code> or <code>error</code> to the
<code>&lt;a></code>. And here is the javascript code:</p><div class="sourceCode" id="cb5"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb5-1"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">// You can replace corsproxy.org (which is a public one)</span></span>
<span id="cb5-2"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="co">// by the one you are hoting.</span></span>
<span id="cb5-3"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> corsproxyurl<span class="op">=</span><span class="st">&#39;https://corsproxy.org/?&#39;</span><span class="op">;</span></span>
<span id="cb5-4"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="kw">async</span> <span class="kw">function</span> <span class="fu">healthchecklink</span>(a) {</span>
<span id="cb5-5"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">var</span> linkurl<span class="op">=</span>a<span class="op">.</span><span class="at">href</span><span class="op">;</span></span>
<span id="cb5-6"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> (linkurl <span class="op">!=</span> <span class="kw">undefined</span>) {</span>
<span id="cb5-7"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">var</span> url <span class="op">=</span> corsproxyurl <span class="op">+</span> <span class="pp">encodeURIComponent</span>(linkurl)<span class="op">;</span></span>
<span id="cb5-8"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">try</span> {</span>
<span id="cb5-9"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-9" aria-hidden="true" tabindex="-1"></a>      <span class="kw">var</span> response <span class="op">=</span> <span class="cf">await</span> <span class="fu">fetch</span>(url<span class="op">,</span> {<span class="dt">method</span><span class="op">:</span> <span class="st">&#39;GET&#39;</span><span class="op">,</span></span>
<span id="cb5-10"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-10" aria-hidden="true" tabindex="-1"></a>                                       <span class="dt">redirect</span><span class="op">:</span> <span class="st">&#39;manual&#39;</span><span class="op">,</span></span>
<span id="cb5-11"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-11" aria-hidden="true" tabindex="-1"></a>                                       <span class="dt">signal</span><span class="op">:</span> AbortSignal<span class="op">.</span><span class="fu">timeout</span>(<span class="dv">3000</span>)</span>
<span id="cb5-12"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-12" aria-hidden="true" tabindex="-1"></a>                                      })<span class="op">;</span></span>
<span id="cb5-13"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-13" aria-hidden="true" tabindex="-1"></a>      <span class="cf">if</span> (response<span class="op">.</span><span class="at">ok</span> <span class="op">||</span> response<span class="op">.</span><span class="at">redirected</span> <span class="op">||</span> response<span class="op">.</span><span class="at">status</span> <span class="op">===</span> <span class="dv">0</span> ) {</span>
<span id="cb5-14"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-14" aria-hidden="true" tabindex="-1"></a>        a<span class="op">.</span><span class="at">classList</span><span class="op">.</span><span class="fu">add</span>(<span class="st">&quot;ok&quot;</span>)<span class="op">;</span></span>
<span id="cb5-15"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-15" aria-hidden="true" tabindex="-1"></a>      } <span class="cf">else</span> {</span>
<span id="cb5-16"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-16" aria-hidden="true" tabindex="-1"></a>        a<span class="op">.</span><span class="at">classList</span><span class="op">.</span><span class="fu">add</span>(<span class="st">&quot;error&quot;</span>)<span class="op">;</span></span>
<span id="cb5-17"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-17" aria-hidden="true" tabindex="-1"></a>      }</span>
<span id="cb5-18"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-18" aria-hidden="true" tabindex="-1"></a>    } <span class="cf">catch</span> (err) {</span>
<span id="cb5-19"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-19" aria-hidden="true" tabindex="-1"></a>      a<span class="op">.</span><span class="at">classList</span><span class="op">.</span><span class="fu">add</span>(<span class="st">&quot;error&quot;</span>)<span class="op">;</span></span>
<span id="cb5-20"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-20" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb5-21"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-21" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb5-22"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-22" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb5-23"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-24"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-24" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">checkhealth</span>() {</span>
<span id="cb5-25"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-25" aria-hidden="true" tabindex="-1"></a>  <span class="kw">var</span> links <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">querySelectorAll</span>(<span class="st">&#39;.healthcheck a&#39;</span>)<span class="op">;</span></span>
<span id="cb5-26"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-26" aria-hidden="true" tabindex="-1"></a>  <span class="cf">for</span> (l <span class="kw">in</span> links) {</span>
<span id="cb5-27"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-27" aria-hidden="true" tabindex="-1"></a>    <span class="fu">healthchecklink</span>(links[l])<span class="op">;</span></span>
<span id="cb5-28"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-28" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb5-29"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-29" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb5-30"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-31"><a href="https://yannesposito.com/posts/0025-a-quick-cors-proxy-in-a-few-lines-of-clojure/index.html#cb5-31" aria-hidden="true" tabindex="-1"></a><span class="fu">checkhealth</span>()<span class="op">;</span></span></code></pre></div><p>Notice the <code>response.status == 0</code>, this is due to one of
my website returning a 303 redirection but some complication make it
returns a status 0. If there were an error it would return an error
status.</p><p>That's it. Another tool I created myself to prevent me using a
service checking for the status of my website and sending me
notifications about it. None of my website is crucial enough not be ok
to wait a few hours to be re-enabled.</p></article></main>
]]></description>
</item>


<item>
<title>Replace docker-compose with nix-shell</title>
<guid>https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html</guid>
<pubDate>Thu, 02 Mar 2023 00:00:00 +0100</pubDate>
<category>blog</category>
<category>static</category>
<description><![CDATA[
<main id="content"><article><p>At work we use <code class="verbatim">docker-compose</code> to run
integration tests on a big project that need to connect to multiple
different databases as well as a few other services. This article is
about how to replace <code class="verbatim">docker-compose</code> by
<code class="verbatim">nix</code> for a local development
environment.</p><h2 id="quick-tutorial">Quick tutorial</h2><h3 id="-nix-shell-fu--level-1-lesson"><code class="verbatim">nix-shell-fu</code> level 1 lesson</h3><p>Let's start with a basic <code class="verbatim">shell.nix</code>
example:</p><div class="sourceCode" id="cb1"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb1-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span><span class="bu">fetchTarball</span> <span class="va">https</span><span class="op">://</span><span class="ss">github.com/NixOS/nixpkgs/archive/22.11.tar.gz</span><span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">with</span> <span class="va">pkgs</span><span class="op">:</span> mkShell</span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span> <span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[</span> hello <span class="op">];</span></span>
<span id="cb1-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39;</span></span>
<span id="cb1-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="st">      echo &quot;Using </span><span class="sc">${</span>hello.name<span class="sc">}</span><span class="st">.&quot;</span></span>
<span id="cb1-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="st">    &#39;&#39;</span><span class="op">;</span></span>
<span id="cb1-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb1-7" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span></code></pre></div><p>And this could be understood in plain English as:</p><blockquote><p>In the packages of nix version 22.11, create a new shell into which
the package <code class="verbatim">hello</code> will be installed. At
the end of the install, run a script that will print the package name.
(Cf <a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#digression">digression</a>)</p></blockquote><p>If you copy/paste this in a <code class="verbatim">shell.nix</code>
file and run <code>nix-shell</code> you get:</p><pre><code>&gt; nix-shell
nix-shell shell.nix
these 53 paths will be fetched (84.69 MiB download, 524.77 MiB unpacked):
  /nix/store/08pckaqznwh0s3822cjp5aji6y1lsm27-libcxx-11.1.0
  ...
  /nix/store/zqcs5xahjxij0c8vfw60lnfb6d979rn2-zlib-1.2.13
copying path &#39;/nix/store/49wn01k9yikhjlxc1ym5b6civ29zz3gv-bash-5.1-p16&#39; from &#39;https://cache.nixos.org&#39;...
...
copying path &#39;/nix/store/4w2rv6s96fwsb4qyw8b9w394010gxriz-stdenv-darwin&#39; from &#39;https://cache.nixos.org&#39;...
Using hello-2.12.1.

[nix-shell:~/tmp/nixplayground]$
</code></pre><p>If you close the session and run it again, it will be much faster and
will only show this:</p><pre><code>❯ nix-shell
Using hello-2.12.1.

[nix-shell:~/tmp/nixplayground]$
</code></pre><p>This is because all dependencies will be cached. OK so, this is level
1 of <em>nix-shell-fu</em>.</p><p>Now, let's start level 2.</p><h3 id="-nix-shell-fu--level-2-lesson--scripting-and-configuring"><code class="verbatim">nix-shell-fu</code> level 2 lesson; scripting and
configuring</h3><p>This time, we want to launch a full service, as a redis docker would
do. So here is a basic shell script which is similar to the previous one
but will request <code class="verbatim">redis</code> as a dependency
instead of <code class="verbatim">hello</code> and also as a launching
script. From there will add a little bit more features.</p><div class="sourceCode" id="cb4"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb4-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span><span class="bu">fetchTarball</span> <span class="va">https</span><span class="op">://</span><span class="ss">github.com/NixOS/nixpkgs/archive/22.11.tar.gz</span><span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb4-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-2" aria-hidden="true" tabindex="-1"></a>  pkgs.mkShell <span class="op">{</span></span>
<span id="cb4-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="co"># must contain buildInputs, nativeBuildInputs and shellHook</span></span>
<span id="cb4-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[</span> pkgs.redis <span class="op">];</span></span>
<span id="cb4-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Post Shell Hook</span></span>
<span id="cb4-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-7" aria-hidden="true" tabindex="-1"></a>    <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39;</span></span>
<span id="cb4-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="st">    echo &quot;Using </span><span class="sc">${</span>pkgs.redis.name<span class="sc">}</span><span class="st"> on port: </span><span class="sc">${</span>port<span class="sc">}</span><span class="st">&quot;</span></span>
<span id="cb4-9"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="st">    redis-server</span></span>
<span id="cb4-10"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="st">  &#39;&#39;</span><span class="op">;</span></span>
<span id="cb4-11"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb4-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span></code></pre></div><p>Again if you run <code>nix-shell</code> here is the result:</p><pre><code>❯ nix-shell
these 2 paths will be fetched (2.08 MiB download, 6.99 MiB unpacked):
  /nix/store/6w4vnaxdx12ccq172i8j5l830mlp8jlg-redis-7.0.5
  /nix/store/b47gmsx9qx0c9vh75wsg8bqq9qd0ad6f-openssl-3.0.7
copying path &#39;/nix/store/b47gmsx9qx0c9vh75wsg8bqq9qd0ad6f-openssl-3.0.7&#39; from &#39;https://cache.nixos.org&#39;...
copying path &#39;/nix/store/6w4vnaxdx12ccq172i8j5l830mlp8jlg-redis-7.0.5&#39; from &#39;https://cache.nixos.org&#39;...
Using redis-7.0.5
97814:C 10 Feb 2023 20:44:36.960 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
97814:C 10 Feb 2023 20:44:36.960 # Redis version=7.0.5, bits=64, commit=00000000, modified=0, pid=97814, just started
97814:C 10 Feb 2023 20:44:36.960 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
97814:M 10 Feb 2023 20:44:36.961 * Increased maximum number of open files to 10032 (it was originally set to 256).
97814:M 10 Feb 2023 20:44:36.961 * monotonic clock: POSIX clock_gettime
                _._
           _.-``__ &#39;&#39;-._
      _.-``    `.  `_.  &#39;&#39;-._           Redis 7.0.5 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ &#39;&#39;-._
 (    &#39;      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|&#39;` _.-&#39;|     Port: 6379
 |    `-._   `._    /     _.-&#39;    |     PID: 97814
  `-._    `-._  `-./  _.-&#39;    _.-&#39;
 |`-._`-._    `-.__.-&#39;    _.-&#39;_.-&#39;|
 |    `-._`-._        _.-&#39;_.-&#39;    |           https://redis.io
  `-._    `-._`-.__.-&#39;_.-&#39;    _.-&#39;
 |`-._`-._    `-.__.-&#39;    _.-&#39;_.-&#39;|
 |    `-._`-._        _.-&#39;_.-&#39;    |
  `-._    `-._`-.__.-&#39;_.-&#39;    _.-&#39;
      `-._    `-.__.-&#39;    _.-&#39;
          `-._        _.-&#39;
              `-.__.-&#39;

97814:M 10 Feb 2023 20:44:36.962 # WARNING: The TCP backlog setting of 511 cannot be enforced because kern.ipc.somaxconn is set to the lower value of 128.
97814:M 10 Feb 2023 20:44:36.962 # Server initialized
97814:M 10 Feb 2023 20:44:36.963 * Ready to accept connections
</code></pre><p>Woo! Redis is started and it works!</p><p>But if you have multiple projects you want to have more control. For
example, we will want to run redis on a specific port. Here is how you
do it:</p><div class="sourceCode" id="cb6"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb6-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span><span class="bu">fetchTarball</span> <span class="va">https</span><span class="op">://</span><span class="ss">github.com/NixOS/nixpkgs/archive/21.05.tar.gz</span><span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb6-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> <span class="va">iport</span> <span class="op">=</span> <span class="dv">16380</span><span class="op">;</span></span>
<span id="cb6-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-3" aria-hidden="true" tabindex="-1"></a>      <span class="va">port</span> <span class="op">=</span> <span class="bu">toString</span> iport<span class="op">;</span></span>
<span id="cb6-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">in</span> pkgs.mkShell <span class="op">{</span></span>
<span id="cb6-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-5" aria-hidden="true" tabindex="-1"></a>    <span class="co"># must contain buildInputs, nativeBuildInputs and shellHook</span></span>
<span id="cb6-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-6" aria-hidden="true" tabindex="-1"></a>    <span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[</span> pkgs.redis <span class="op">];</span></span>
<span id="cb6-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-8" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Post Shell Hook</span></span>
<span id="cb6-9"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39;</span></span>
<span id="cb6-10"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="st">    echo &quot;Using </span><span class="sc">${</span>pkgs.redis.name<span class="sc">}</span><span class="st"> on port </span><span class="sc">${</span>port<span class="sc">}</span><span class="st">&quot;</span></span>
<span id="cb6-11"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="st">    redis-server --port </span><span class="sc">${</span>port<span class="sc">}</span></span>
<span id="cb6-12"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="st">  &#39;&#39;</span><span class="op">;</span></span>
<span id="cb6-13"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb6-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span></code></pre></div><p>And here is the result:</p><pre><code>&gt; rm dump.rdb
&gt; nix-shell
Using redis-6.2.3 on port 16380
1785:C 10 Feb 2023 20:50:00.880 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1785:C 10 Feb 2023 20:50:00.880 # Redis version=6.2.3, bits=64, commit=00000000, modified=0, pid=1785, just started
1785:C 10 Feb 2023 20:50:00.880 # Configuration loaded
1785:M 10 Feb 2023 20:50:00.880 * Increased maximum number of open files to 10032 (it was originally set to 256).
1785:M 10 Feb 2023 20:50:00.880 * monotonic clock: POSIX clock_gettime
                _._
           _.-``__ &#39;&#39;-._
      _.-``    `.  `_.  &#39;&#39;-._           Redis 6.2.3 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ &#39;&#39;-._
 (    &#39;      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|&#39;` _.-&#39;|     Port: 16380
 |    `-._   `._    /     _.-&#39;    |     PID: 1785
  `-._    `-._  `-./  _.-&#39;    _.-&#39;
 |`-._`-._    `-.__.-&#39;    _.-&#39;_.-&#39;|
 |    `-._`-._        _.-&#39;_.-&#39;    |           https://redis.io
  `-._    `-._`-.__.-&#39;_.-&#39;    _.-&#39;
 |`-._`-._    `-.__.-&#39;    _.-&#39;_.-&#39;|
 |    `-._`-._        _.-&#39;_.-&#39;    |
  `-._    `-._`-.__.-&#39;_.-&#39;    _.-&#39;
      `-._    `-.__.-&#39;    _.-&#39;
          `-._        _.-&#39;
              `-.__.-&#39;

1785:M 10 Feb 2023 20:50:00.881 # Server initialized
1785:M 10 Feb 2023 20:50:00.881 * Ready to accept connections
</code></pre><p>Woo! We control the port from the file. That's nice.</p><p>But, has you might have noticed, when you quit the session it dumps
the DB as the file <code class="verbatim">dump.rdb</code>. What we would
like is to keep all the state in a local directory that would be easy to
delete.</p><p>To achieve this, instead of passing argument to the redis command
line we will use a local config file to use.</p><div class="sourceCode" id="cb8"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb8-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span><span class="bu">fetchTarball</span> <span class="va">https</span><span class="op">://</span><span class="ss">github.com/NixOS/nixpkgs/archive/22.11.tar.gz</span><span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb8-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="va">iport</span> <span class="op">=</span> <span class="dv">16380</span><span class="op">;</span></span>
<span id="cb8-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-3" aria-hidden="true" tabindex="-1"></a>    <span class="va">port</span> <span class="op">=</span> <span class="bu">toString</span> iport<span class="op">;</span></span>
<span id="cb8-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> pkgs.mkShell <span class="op">(</span><span class="kw">rec</span> <span class="op">{</span></span>
<span id="cb8-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-5" aria-hidden="true" tabindex="-1"></a>  <span class="co"># ENV Variables the directory to put all the DATA</span></span>
<span id="cb8-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-6" aria-hidden="true" tabindex="-1"></a>  <span class="va">REDIS_DATA</span> <span class="op">=</span> <span class="st">&quot;</span><span class="sc">${</span><span class="bu">toString</span> <span class="ss">./.</span><span class="sc">}</span><span class="st">/.redis&quot;</span><span class="op">;</span></span>
<span id="cb8-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-7" aria-hidden="true" tabindex="-1"></a>  <span class="co"># the config file, as we use REDIS_DATA variable we just declared in the</span></span>
<span id="cb8-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-8" aria-hidden="true" tabindex="-1"></a>  <span class="co"># same nix set, we need to use rec</span></span>
<span id="cb8-9"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-9" aria-hidden="true" tabindex="-1"></a>  <span class="va">redisConf</span> <span class="op">=</span> pkgs.writeText <span class="st">&quot;redis.conf&quot;</span></span>
<span id="cb8-10"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-10" aria-hidden="true" tabindex="-1"></a>                             <span class="st">&#39;&#39;</span></span>
<span id="cb8-11"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-11" aria-hidden="true" tabindex="-1"></a><span class="st">                             port </span><span class="sc">${</span>port<span class="sc">}</span></span>
<span id="cb8-12"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-12" aria-hidden="true" tabindex="-1"></a><span class="st">                             dbfilename redis.db</span></span>
<span id="cb8-13"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-13" aria-hidden="true" tabindex="-1"></a><span class="st">                             dir </span><span class="sc">${</span>REDIS_DATA<span class="sc">}</span></span>
<span id="cb8-14"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-14" aria-hidden="true" tabindex="-1"></a><span class="st">                             &#39;&#39;</span><span class="op">;</span></span>
<span id="cb8-15"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-16"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-16" aria-hidden="true" tabindex="-1"></a>  <span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[</span> pkgs.redis <span class="op">];</span></span>
<span id="cb8-17"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-18"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-18" aria-hidden="true" tabindex="-1"></a>  <span class="co"># Post Shell Hook</span></span>
<span id="cb8-19"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-19" aria-hidden="true" tabindex="-1"></a>  <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39;</span></span>
<span id="cb8-20"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-20" aria-hidden="true" tabindex="-1"></a><span class="st">    echo &quot;Using </span><span class="sc">${</span>pkgs.redis.name<span class="sc">}</span><span class="st"> on port: </span><span class="sc">${</span>port<span class="sc">}</span><span class="st">&quot;</span></span>
<span id="cb8-21"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-22"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-22" aria-hidden="true" tabindex="-1"></a><span class="st">    [ ! -d $REDIS_DATA ] \</span></span>
<span id="cb8-23"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-23" aria-hidden="true" tabindex="-1"></a><span class="st">      &amp;&amp; mkdir -p $REDIS_DATA</span></span>
<span id="cb8-24"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-24" aria-hidden="true" tabindex="-1"></a><span class="st">    cat &quot;$redisConf&quot; &gt; $REDIS_DATA/redis.conf</span></span>
<span id="cb8-25"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-25" aria-hidden="true" tabindex="-1"></a><span class="st">    alias redisstop=&quot;echo &#39;Stopping Redis&#39;; redis-cli -p </span><span class="sc">${</span>port<span class="sc">}</span><span class="st"> shutdown; rm -rf $REDIS_DATA&quot;</span></span>
<span id="cb8-26"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-26" aria-hidden="true" tabindex="-1"></a><span class="st">    nohup redis-server $REDIS_DATA/redis.conf &gt; /dev/null 2&gt;&amp;1 &amp;</span></span>
<span id="cb8-27"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-27" aria-hidden="true" tabindex="-1"></a><span class="st">    echo &quot;When finished just run redisstop &amp;&amp; exit&quot;</span></span>
<span id="cb8-28"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-28" aria-hidden="true" tabindex="-1"></a><span class="st">    trap redisstop EXIT</span></span>
<span id="cb8-29"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-29" aria-hidden="true" tabindex="-1"></a><span class="st">  &#39;&#39;</span><span class="op">;</span></span>
<span id="cb8-30"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb8-30" aria-hidden="true" tabindex="-1"></a><span class="op">})</span></span></code></pre></div><p>And here is a full session using this <code class="verbatim">shell.nix</code>:</p><pre><code>&gt; nix-shell
Using redis-6.2.3 on port: 16380
When finished just run redisstop &amp;&amp; exit

-------------------------------
[nix-shell:~/tmp/nixplayground]$ redis-cli -p 16380
127.0.0.1:16380&gt; help
redis-cli 6.2.3
To get help about Redis commands type:
      &quot;help @&lt;group&gt;&quot; to get a list of commands in &lt;group&gt;
      &quot;help &lt;command&gt;&quot; for help on &lt;command&gt;
      &quot;help &lt;tab&gt;&quot; to get a list of possible help topics
      &quot;quit&quot; to exit

To set redis-cli preferences:
      &quot;:set hints&quot; enable online hints
      &quot;:set nohints&quot; disable online hints
Set your preferences in ~/.redisclirc
127.0.0.1:16380&gt;

-------------------------------
[nix-shell:~/tmp/nixplayground]$ ls -a
.  ..  .redis  shell.nix

-------------------------------
[nix-shell:~/tmp/nixplayground]$ find .redis
.redis
.redis/redis.conf

-------------------------------
[nix-shell:~/tmp/nixplayground]$ redis-cli -p 16380 shutdown
[1]+  Done                    nohup redis-server $REDIS_DATA/redis.conf &gt; /dev/null 2&gt;&amp;1

-------------------------------
[nix-shell:~/tmp/nixplayground]$ find .redis
.redis
.redis/redis.db
.redis/redis.conf

-------------------------------
[nix-shell:~/tmp/nixplayground]$ redisstop
Stopping Redis
Could not connect to Redis at 127.0.0.1:16380: Connection refused

-------------------------------
[nix-shell:~/tmp/nixplayground]$ ls -a
.  ..  shell.nix
</code></pre><p>So with this version all data related to redis is saved into the
local <code class="verbatim">.redis</code> directory. And in the nix
shell we provide a command <code class="verbatim">redisstop</code> that
once invoked, shutdown redis, then purge all redis related data (as you
would like in a development environment). Also, as compared to previous
version, redis is launched in background so you could run commands in
your nix shell.</p><p>Notice I also run <code>redisstop</code> command on exit of the
nix-shell. So when you close the nix-shell redis is stopped and the DB
state is cleaned up.</p><h2 id="-nix-shell-fu--level-3-lesson--composability">Composable <code class="verbatim">nix-shell</code></h2><p>As a quick recap you now have a boilerplate to create new <code class="verbatim">shell.nix</code>:</p><div class="sourceCode" id="cb10"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb10-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span> ... <span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb10-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb10-2" aria-hidden="true" tabindex="-1"></a>mkShell <span class="op">{</span> <span class="va">MY_ENV_VAR_1</span> <span class="op">=</span> ...<span class="op">;</span></span>
<span id="cb10-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb10-3" aria-hidden="true" tabindex="-1"></a>          <span class="va">MY_ENV_VAR_2</span> <span class="op">=</span> ...<span class="op">;</span></span>
<span id="cb10-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb10-4" aria-hidden="true" tabindex="-1"></a>          <span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[</span> dependency<span class="dv">-1</span> ... dependency-n <span class="op">];</span></span>
<span id="cb10-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb10-5" aria-hidden="true" tabindex="-1"></a>          <span class="va">nativeBuildInputs</span> <span class="op">=</span> <span class="op">[</span> dependency<span class="dv">-1</span> ... dependency-n <span class="op">];</span></span>
<span id="cb10-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb10-6" aria-hidden="true" tabindex="-1"></a>          <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39; command_to_run_after_init &#39;&#39;</span><span class="op">;</span></span>
<span id="cb10-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb10-7" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span></code></pre></div><p>But if I give you two such <code class="verbatim">shell.nix</code>
files, would you be able to compose them? Unfortunately, not directly.
To solve the problem we will replace this boilerplate by another one
that do not directly uses <code class="verbatim">mkShell</code>. And in
order to make it fully composable, we will also need to narrow the
environment variables declaration in a sub field:</p><div class="sourceCode" id="cb11"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb11-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span> ... <span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb11-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="va">env</span> <span class="op">=</span> <span class="op">{</span> <span class="va">PGDATA</span> <span class="op">=</span> ...<span class="op">;</span> <span class="op">}</span></span>
<span id="cb11-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb11-3" aria-hidden="true" tabindex="-1"></a>in <span class="op">{</span> <span class="kw">inherit</span> env<span class="op">;</span> <span class="co"># equivalent to env = env;</span></span>
<span id="cb11-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb11-4" aria-hidden="true" tabindex="-1"></a>     <span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[</span> dependency<span class="dv">-1</span> ... dependency-n <span class="op">];</span></span>
<span id="cb11-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb11-5" aria-hidden="true" tabindex="-1"></a>     <span class="va">nativeBuildInputs</span> <span class="op">=</span> <span class="op">[</span> dependency<span class="dv">-1</span> ... dependency-n <span class="op">];</span></span>
<span id="cb11-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb11-6" aria-hidden="true" tabindex="-1"></a>     <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39; some_command $PG_DATA &#39;&#39;</span><span class="op">;</span></span>
<span id="cb11-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb11-7" aria-hidden="true" tabindex="-1"></a>   <span class="op">}</span></span></code></pre></div><p>With this, we can compose two nix set into a single merged one that
will be suitable to pass as argument to <code>mkShell</code>. Another
minor detail, but important one. In bash, the command <code>trap</code>
do not accumulate but replace the function. For our need, we want to run
all stop function on exit. So the <code>trap</code> directive added in
the shell hook does not compose naturally. This is why we add a <code class="verbatim">stop</code> value that will contain the name of the
bash function to call to stop and cleanup a service.</p><p>Finally the main structure for each of our service will look like
this <strong>nix service boilerplate</strong>:</p><div class="sourceCode" id="cb12"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb12-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span> ... <span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb12-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="va">env</span> <span class="op">=</span> <span class="op">{</span> <span class="va">MY_SERVICE_ENV_VAR</span> <span class="op">=</span> ...<span class="op">;</span> <span class="op">}</span></span>
<span id="cb12-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb12-3" aria-hidden="true" tabindex="-1"></a>in <span class="op">{</span> <span class="kw">inherit</span> env<span class="op">;</span> <span class="co"># equivalent to env = env;</span></span>
<span id="cb12-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb12-4" aria-hidden="true" tabindex="-1"></a>     <span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[</span> dependency<span class="dv">-1</span> ... dependency-n <span class="op">];</span></span>
<span id="cb12-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb12-5" aria-hidden="true" tabindex="-1"></a>     <span class="va">nativeBuildInputs</span> <span class="op">=</span> <span class="op">[</span> dependency<span class="dv">-1</span> ... dependency-n <span class="op">];</span></span>
<span id="cb12-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb12-6" aria-hidden="true" tabindex="-1"></a>     <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39; my_command $MY_SERVICE_ENV_VAR &#39;&#39;</span><span class="op">;</span></span>
<span id="cb12-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb12-7" aria-hidden="true" tabindex="-1"></a>     <span class="va">stop</span> <span class="op">=</span> <span class="st">&quot;stop_my_service&quot;</span></span>
<span id="cb12-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb12-8" aria-hidden="true" tabindex="-1"></a>   <span class="er">}</span></span></code></pre></div><p>So let's start easy. To run a single shell script like this with
<code class="verbatim">nix-shell</code>, you should put your service
specific nix file in a <code class="verbatim">service.nix</code> file
and create a <code class="verbatim">shell.nix</code> file that contains
something like:</p><div class="sourceCode" id="cb13"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb13-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span><span class="bu">fetchTarball</span> <span class="va">https</span><span class="op">://</span><span class="ss">github.com/NixOS/nixpkgs/archive/22.11.tar.gz</span><span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb13-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="va">service</span> <span class="op">=</span> <span class="bu">import</span> <span class="ss">./service.nix</span> <span class="op">{</span> <span class="kw">inherit</span> pkgs<span class="op">;</span> <span class="op">};</span></span>
<span id="cb13-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> <span class="kw">with</span> service<span class="op">;</span> pkgs.mkShell <span class="op">(</span> env <span class="op">//</span></span>
<span id="cb13-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb13-4" aria-hidden="true" tabindex="-1"></a>   <span class="op">{</span></span>
<span id="cb13-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb13-5" aria-hidden="true" tabindex="-1"></a>     <span class="va">buildInputs</span>       <span class="op">=</span> buildInputs<span class="op">;</span></span>
<span id="cb13-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb13-6" aria-hidden="true" tabindex="-1"></a>     <span class="va">nativeBuildInputs</span> <span class="op">=</span> nativeBuildInputs <span class="op">;</span></span>
<span id="cb13-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb13-7" aria-hidden="true" tabindex="-1"></a>     <span class="va">shellHook</span>         <span class="op">=</span> shellHook<span class="op">;</span></span>
<span id="cb13-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb13-8" aria-hidden="true" tabindex="-1"></a>   <span class="op">})</span></span></code></pre></div><p>Now, if you would like to run nix shell for multiple files, here is a
first qui solution:</p><div class="sourceCode" id="cb14"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb14-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span>...<span class="op">)</span> <span class="op">{}}</span>:</span>
<span id="cb14-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb14-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-3" aria-hidden="true" tabindex="-1"></a>  <span class="co"># merge all the env sets</span></span>
<span id="cb14-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-4" aria-hidden="true" tabindex="-1"></a>  <span class="va">mergedEnvs</span> <span class="op">=</span> <span class="bu">builtins</span>.foldl&#39; <span class="op">(</span><span class="va">acc</span><span class="op">:</span> <span class="va">e</span><span class="op">:</span> acc <span class="op">//</span> e<span class="op">)</span> <span class="op">{}</span> envs<span class="op">;</span></span>
<span id="cb14-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-6" aria-hidden="true" tabindex="-1"></a>  <span class="co"># merge all the confs by accumulating the dependencies</span></span>
<span id="cb14-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-7" aria-hidden="true" tabindex="-1"></a>  <span class="co"># and concatenating the shell hooks.</span></span>
<span id="cb14-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-8" aria-hidden="true" tabindex="-1"></a>  <span class="va">mergedConfs</span> <span class="op">=</span></span>
<span id="cb14-9"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-9" aria-hidden="true" tabindex="-1"></a>    <span class="bu">builtins</span>.foldl&#39;</span>
<span id="cb14-10"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-10" aria-hidden="true" tabindex="-1"></a>      <span class="op">(</span><span class="va">acc</span><span class="op">:</span> <span class="op">{</span><span class="va">buildInputs</span> <span class="op">?</span> <span class="op">[],</span> <span class="va">nativeBuildInputs</span> <span class="op">?</span> <span class="op">[],</span> <span class="va">shellHook</span> <span class="op">?</span> <span class="st">&quot;&quot;</span><span class="op">,</span> <span class="op">...}</span>:</span>
<span id="cb14-11"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-11" aria-hidden="true" tabindex="-1"></a>        <span class="op">{</span> <span class="va">buildInputs</span> <span class="op">=</span> acc.buildInputs <span class="op">++</span> buildInputs<span class="op">;</span></span>
<span id="cb14-12"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-12" aria-hidden="true" tabindex="-1"></a>          <span class="va">nativeBuildInputs</span> <span class="op">=</span> acc.nativeBuildInputs <span class="op">++</span> nativeBuildInputs<span class="op">;</span></span>
<span id="cb14-13"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-13" aria-hidden="true" tabindex="-1"></a>          <span class="va">shellHook</span> <span class="op">=</span> acc.shellHook <span class="op">+</span> shellHook<span class="op">;</span></span>
<span id="cb14-14"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-14" aria-hidden="true" tabindex="-1"></a>        <span class="op">})</span></span>
<span id="cb14-15"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-15" aria-hidden="true" tabindex="-1"></a>      emptyConf</span>
<span id="cb14-16"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-16" aria-hidden="true" tabindex="-1"></a>      confs<span class="op">;</span></span>
<span id="cb14-17"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb14-17" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> mkShell <span class="op">(</span>mergedEnvs <span class="op">//</span> mergedConfs<span class="op">)</span></span></code></pre></div><p>And now, here is the full solution that also deal with other minor
details like importing the files and dealing with the exit of the
shell:</p><div class="sourceCode" id="cb15"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb15-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">mergeShellConfs</span> <span class="op">=</span></span>
<span id="cb15-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-2" aria-hidden="true" tabindex="-1"></a>    <span class="co"># imports should contain a list of nix files</span></span>
<span id="cb15-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="va">pkgs</span><span class="op">,</span> <span class="va">imports</span> <span class="op">}</span>:</span>
<span id="cb15-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="va">confs</span> <span class="op">=</span> <span class="bu">map</span> <span class="op">(</span><span class="va">f</span><span class="op">:</span> <span class="bu">import</span> f <span class="op">{</span> <span class="kw">inherit</span> pkgs<span class="op">;</span> <span class="op">})</span> imports<span class="op">;</span></span>
<span id="cb15-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-5" aria-hidden="true" tabindex="-1"></a>        <span class="va">envs</span>  <span class="op">=</span> <span class="bu">map</span> <span class="op">({</span><span class="va">env</span> <span class="op">?</span> <span class="op">{},</span> <span class="op">...}</span>: env<span class="op">)</span> confs<span class="op">;</span></span>
<span id="cb15-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-7" aria-hidden="true" tabindex="-1"></a>        <span class="co"># list the name of a command to stop a service (if none provided just use &#39;:&#39; which mean noop)</span></span>
<span id="cb15-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-8" aria-hidden="true" tabindex="-1"></a>        <span class="va">stops</span>  <span class="op">=</span> <span class="bu">map</span> <span class="op">({</span><span class="va">stop</span> <span class="op">?</span> <span class="st">&quot;:&quot;</span><span class="op">,</span> <span class="op">...}</span>: stop<span class="op">)</span> confs<span class="op">;</span></span>
<span id="cb15-9"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-10"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-10" aria-hidden="true" tabindex="-1"></a>        <span class="co"># we want to stop all services on exit</span></span>
<span id="cb15-11"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-11" aria-hidden="true" tabindex="-1"></a>        <span class="va">stopCmd</span> <span class="op">=</span> <span class="bu">builtins</span>.concatStringsSep <span class="st">&quot; &amp;&amp; &quot;</span> stops<span class="op">;</span></span>
<span id="cb15-12"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-13"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-13" aria-hidden="true" tabindex="-1"></a>        <span class="co"># we would like to add a shellHook to cleanup the service that will call</span></span>
<span id="cb15-14"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-14" aria-hidden="true" tabindex="-1"></a>        <span class="co"># all cleaning-up function declared in sub-shells</span></span>
<span id="cb15-15"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-15" aria-hidden="true" tabindex="-1"></a>        <span class="va">lastConf</span> <span class="op">=</span></span>
<span id="cb15-16"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-16" aria-hidden="true" tabindex="-1"></a>          <span class="op">{</span> <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39;</span></span>
<span id="cb15-17"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-17" aria-hidden="true" tabindex="-1"></a><span class="st">                        stopall() { </span><span class="sc">${</span>stopCmd<span class="sc">}</span><span class="st">; }</span></span>
<span id="cb15-18"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-18" aria-hidden="true" tabindex="-1"></a><span class="st">                        echo &quot;You can manually stop all services by calling stopall&quot;</span></span>
<span id="cb15-19"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-19" aria-hidden="true" tabindex="-1"></a><span class="st">                        trap stopall EXIT</span></span>
<span id="cb15-20"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-20" aria-hidden="true" tabindex="-1"></a><span class="st">                        &#39;&#39;</span><span class="op">;</span></span>
<span id="cb15-21"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-21" aria-hidden="true" tabindex="-1"></a>          <span class="op">};</span></span>
<span id="cb15-22"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-23"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-23" aria-hidden="true" tabindex="-1"></a>        <span class="co"># merge Environment variables needed for other shell environments</span></span>
<span id="cb15-24"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-24" aria-hidden="true" tabindex="-1"></a>        <span class="va">mergedEnvs</span> <span class="op">=</span> <span class="bu">builtins</span>.foldl&#39; <span class="op">(</span><span class="va">acc</span><span class="op">:</span> <span class="va">e</span><span class="op">:</span> acc <span class="op">//</span> e<span class="op">)</span> <span class="op">{}</span> envs<span class="op">;</span></span>
<span id="cb15-25"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-26"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-26" aria-hidden="true" tabindex="-1"></a>        <span class="co"># zeroConf is the minimal empty configuration needed</span></span>
<span id="cb15-27"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-27" aria-hidden="true" tabindex="-1"></a>        <span class="va">zeroConf</span> <span class="op">=</span> <span class="op">{</span><span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[];</span> <span class="va">nativeBuildInputs</span> <span class="op">=</span> <span class="op">[];</span> <span class="va">shellHook</span><span class="op">=</span><span class="st">&quot;&quot;</span><span class="op">;};</span></span>
<span id="cb15-28"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-29"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-29" aria-hidden="true" tabindex="-1"></a>        <span class="co"># merge all confs by appending buildInputs and nativeBuildInputs</span></span>
<span id="cb15-30"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-30" aria-hidden="true" tabindex="-1"></a>        <span class="co"># and by concatenating the shellHooks</span></span>
<span id="cb15-31"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-31" aria-hidden="true" tabindex="-1"></a>        <span class="va">mergedConfs</span> <span class="op">=</span></span>
<span id="cb15-32"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-32" aria-hidden="true" tabindex="-1"></a>          <span class="bu">builtins</span>.foldl&#39;</span>
<span id="cb15-33"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-33" aria-hidden="true" tabindex="-1"></a>            <span class="op">(</span><span class="va">acc</span><span class="op">:</span> <span class="op">{</span><span class="va">buildInputs</span> <span class="op">?</span> <span class="op">[],</span> <span class="va">nativeBuildInputs</span> <span class="op">?</span> <span class="op">[],</span> <span class="va">shellHook</span> <span class="op">?</span> <span class="st">&quot;&quot;</span><span class="op">,</span> <span class="op">...}</span>:</span>
<span id="cb15-34"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-34" aria-hidden="true" tabindex="-1"></a>              <span class="op">{</span> <span class="va">buildInputs</span> <span class="op">=</span> acc.buildInputs <span class="op">++</span> buildInputs<span class="op">;</span></span>
<span id="cb15-35"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-35" aria-hidden="true" tabindex="-1"></a>                <span class="va">nativeBuildInputs</span> <span class="op">=</span> acc.nativeBuildInputs <span class="op">++</span> nativeBuildInputs<span class="op">;</span></span>
<span id="cb15-36"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-36" aria-hidden="true" tabindex="-1"></a>                <span class="va">shellHook</span> <span class="op">=</span> acc.shellHook <span class="op">+</span> shellHook<span class="op">;</span></span>
<span id="cb15-37"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-37" aria-hidden="true" tabindex="-1"></a>              <span class="op">})</span></span>
<span id="cb15-38"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-38" aria-hidden="true" tabindex="-1"></a>            zeroConf</span>
<span id="cb15-39"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-39" aria-hidden="true" tabindex="-1"></a>            <span class="op">(</span>confs <span class="op">++</span> <span class="op">[</span>lastConf<span class="op">]);</span></span>
<span id="cb15-40"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-41"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-41" aria-hidden="true" tabindex="-1"></a>    <span class="kw">in</span> <span class="op">(</span>mergedEnvs <span class="op">//</span> mergedConfs<span class="op">);</span></span>
<span id="cb15-42"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb15-42" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div><p>So I put this function declaration in a file named <code class="verbatim">./nix/merge-shell.nix</code>. And I have a <code class="verbatim">pg.nix</code> as well as a <code class="verbatim">redis.nix</code> file in the <code class="verbatim">nix</code> directory. On the root of the project the
main <code class="verbatim">shell.nix</code> looks like:</p><div class="sourceCode" id="cb16"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb16-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span><span class="bu">fetchTarball</span> <span class="va">https</span><span class="op">://</span><span class="ss">github.com/NixOS/nixpkgs/archive/22.11.tar.gz</span><span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb16-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span></span>
<span id="cb16-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-3" aria-hidden="true" tabindex="-1"></a>  <span class="co"># we import the file, and rename the function mergeShellConfs as mergeShells</span></span>
<span id="cb16-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-4" aria-hidden="true" tabindex="-1"></a>  <span class="va">mergeShells</span> <span class="op">=</span> <span class="op">(</span><span class="bu">import</span> <span class="ss">./nix/merge-shell.nix</span><span class="op">)</span>.mergeShellConfs<span class="op">;</span></span>
<span id="cb16-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-5" aria-hidden="true" tabindex="-1"></a>  <span class="co"># we call mergeShells</span></span>
<span id="cb16-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-6" aria-hidden="true" tabindex="-1"></a>  <span class="va">mergedShellConfs</span> <span class="op">=</span></span>
<span id="cb16-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-7" aria-hidden="true" tabindex="-1"></a>    mergeShells <span class="op">{</span> <span class="kw">inherit</span> pkgs<span class="op">;</span></span>
<span id="cb16-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-8" aria-hidden="true" tabindex="-1"></a>                  <span class="co"># imports = [ ./nix/pg.nix ./nix/redis.nix ];</span></span>
<span id="cb16-9"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-9" aria-hidden="true" tabindex="-1"></a>                  <span class="va">imports</span> <span class="op">=</span> <span class="op">[</span> <span class="ss">./nix/pg.nix</span> <span class="ss">./nix/redis.nix</span> <span class="op">];</span></span>
<span id="cb16-10"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-10" aria-hidden="true" tabindex="-1"></a>                <span class="op">};</span></span>
<span id="cb16-11"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb16-11" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> pkgs.mkShell mergedShellConfs</span></code></pre></div><p>And, that's it. Now when I run <code class="verbatim">nix-shell</code> it launch both Postgresql and Redis,
and when I quit the shell, the state is cleaned up. Both postgres and
redis are shutdown and the local files are erased.</p><p>I hope this could be useful to someone else.</p><h2 id="appendix">Appendix</h2><h3 id="--digression---digression"><span id="digression"></span>Digression</h3><p>In fact, this is a bit more complex than "just that". The reality is
a bit more complex. The nix language is "pure", meaning, if you run the
nix evaluation multiple times, it will always evaluate to the exact same
value. But here, this block represent a function. The function takes as
input a "nix set" (which you can see as an associative array, or a
hash-map or also a javascript object depending on your preference), and
this set is expected to contain a field named <code class="verbatim">pkgs</code>. If <code class="verbatim">pkgs</code> is
not provided, it will use the set from the stable version 22.11 of
nixpkgs by downloading them from github archive. The second part of the
function generate "something" that is returned by an internal function
of the standard library provided by <code class="verbatim">nix</code>
which is named <code class="verbatim">mkShell</code>. So mainly, <code class="verbatim">mkShell</code> is a helper function that will generate
what nix calls a <em><a href="https://blog.ielliott.io/nix-docs/derivation.html">derivation</a></em>.
Mainly, we don't really care about exactly what is a
<em>derivation</em>. This is an internal to nix representation that
could be finally used by different nix tools for different things.
Typically, installing a package, running a local development environment
with nix-shell or nix develop, etc…</p><p>So the important detail to remember is that we can manipulate the
parameter we pass to the functions <code class="verbatim">derivation</code>, <code class="verbatim">mkDerivation</code> and <code class="verbatim">mkShell</code>, but we have no mechanism to manipulate
directly <code class="verbatim">derivation</code>. So in order to make
that composable, you need to call the <code class="verbatim">derivation</code> internal function at the very end
only.</p><p>The argument of all these functions are <em>nix sets</em></p><h3 id="the-full-nix-files-for-postgres">The full nix files for
postgres</h3><p>For postgres:</p><div class="sourceCode" id="cb17"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb17-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">}</span>:</span>
<span id="cb17-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> <span class="va">iport</span> <span class="op">=</span> <span class="dv">15432</span><span class="op">;</span></span>
<span id="cb17-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-3" aria-hidden="true" tabindex="-1"></a>      <span class="va">port</span> <span class="op">=</span> <span class="bu">toString</span> iport<span class="op">;</span></span>
<span id="cb17-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-4" aria-hidden="true" tabindex="-1"></a>      <span class="va">pguser</span> <span class="op">=</span> <span class="st">&quot;pguser&quot;</span><span class="op">;</span></span>
<span id="cb17-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-5" aria-hidden="true" tabindex="-1"></a>      <span class="va">pgpass</span> <span class="op">=</span> <span class="st">&quot;pgpass&quot;</span><span class="op">;</span></span>
<span id="cb17-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-6" aria-hidden="true" tabindex="-1"></a>      <span class="va">pgdb</span> <span class="op">=</span> <span class="st">&quot;iroh&quot;</span><span class="op">;</span></span>
<span id="cb17-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-7" aria-hidden="true" tabindex="-1"></a>      <span class="co"># env should contain all variable you need to configure correctly mkShell</span></span>
<span id="cb17-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-8" aria-hidden="true" tabindex="-1"></a>      <span class="co"># so ENV_VAR, but also any other kind of variables.</span></span>
<span id="cb17-9"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-9" aria-hidden="true" tabindex="-1"></a>      <span class="va">env</span> <span class="op">=</span>  <span class="op">{</span></span>
<span id="cb17-10"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-10" aria-hidden="true" tabindex="-1"></a>        <span class="va">postgresConf</span> <span class="op">=</span></span>
<span id="cb17-11"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-11" aria-hidden="true" tabindex="-1"></a>          pkgs.writeText <span class="st">&quot;postgresql.conf&quot;</span></span>
<span id="cb17-12"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-12" aria-hidden="true" tabindex="-1"></a>            <span class="st">&#39;&#39;</span></span>
<span id="cb17-13"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-13" aria-hidden="true" tabindex="-1"></a><span class="st">        # Add Custom Settings</span></span>
<span id="cb17-14"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-14" aria-hidden="true" tabindex="-1"></a><span class="st">        log_min_messages = warning</span></span>
<span id="cb17-15"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-15" aria-hidden="true" tabindex="-1"></a><span class="st">        log_min_error_statement = error</span></span>
<span id="cb17-16"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-16" aria-hidden="true" tabindex="-1"></a><span class="st">        log_min_duration_statement = 100  # ms</span></span>
<span id="cb17-17"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-17" aria-hidden="true" tabindex="-1"></a><span class="st">        log_connections = on</span></span>
<span id="cb17-18"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-18" aria-hidden="true" tabindex="-1"></a><span class="st">        log_disconnections = on</span></span>
<span id="cb17-19"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-19" aria-hidden="true" tabindex="-1"></a><span class="st">        log_duration = on</span></span>
<span id="cb17-20"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-20" aria-hidden="true" tabindex="-1"></a><span class="st">        #log_line_prefix = &#39;[] &#39;</span></span>
<span id="cb17-21"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-21" aria-hidden="true" tabindex="-1"></a><span class="st">        log_timezone = &#39;UTC&#39;</span></span>
<span id="cb17-22"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-22" aria-hidden="true" tabindex="-1"></a><span class="st">        log_statement = &#39;all&#39;</span></span>
<span id="cb17-23"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-23" aria-hidden="true" tabindex="-1"></a><span class="st">        log_directory = &#39;pg_log&#39;</span></span>
<span id="cb17-24"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-24" aria-hidden="true" tabindex="-1"></a><span class="st">        log_filename = &#39;postgresql-%Y-%m-%d_%H%M%S.log&#39;</span></span>
<span id="cb17-25"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-25" aria-hidden="true" tabindex="-1"></a><span class="st">        logging_collector = on</span></span>
<span id="cb17-26"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-26" aria-hidden="true" tabindex="-1"></a><span class="st">        log_min_error_statement = error</span></span>
<span id="cb17-27"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-27" aria-hidden="true" tabindex="-1"></a><span class="st">      &#39;&#39;</span><span class="op">;</span></span>
<span id="cb17-28"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-29"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-29" aria-hidden="true" tabindex="-1"></a>        <span class="va">postgresInitScript</span> <span class="op">=</span></span>
<span id="cb17-30"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-30" aria-hidden="true" tabindex="-1"></a>          pkgs.writeText <span class="st">&quot;init.sql&quot;</span></span>
<span id="cb17-31"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-31" aria-hidden="true" tabindex="-1"></a>            <span class="st">&#39;&#39;</span></span>
<span id="cb17-32"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-32" aria-hidden="true" tabindex="-1"></a><span class="st">        CREATE DATABASE </span><span class="sc">${</span>pgdb<span class="sc">}</span><span class="st">;</span></span>
<span id="cb17-33"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-33" aria-hidden="true" tabindex="-1"></a><span class="st">        CREATE USER </span><span class="sc">${</span>pguser<span class="sc">}</span><span class="st"> WITH ENCRYPTED PASSWORD &#39;</span><span class="sc">${</span>pgpass<span class="sc">}</span><span class="st">&#39;;</span></span>
<span id="cb17-34"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-34" aria-hidden="true" tabindex="-1"></a><span class="st">        GRANT ALL PRIVILEGES ON DATABASE </span><span class="sc">${</span>pgdb<span class="sc">}</span><span class="st"> TO </span><span class="sc">${</span>pguser<span class="sc">}</span><span class="st">;</span></span>
<span id="cb17-35"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-35" aria-hidden="true" tabindex="-1"></a><span class="st">        &#39;&#39;</span><span class="op">;</span></span>
<span id="cb17-36"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-37"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-37" aria-hidden="true" tabindex="-1"></a>        <span class="va">PGDATA</span> <span class="op">=</span> <span class="st">&quot;</span><span class="sc">${</span><span class="bu">toString</span> <span class="ss">./.</span><span class="sc">}</span><span class="st">/.pg&quot;</span><span class="op">;</span></span>
<span id="cb17-38"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-38" aria-hidden="true" tabindex="-1"></a>      <span class="op">};</span></span>
<span id="cb17-39"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-39" aria-hidden="true" tabindex="-1"></a>  <span class="kw">in</span> env <span class="op">//</span> <span class="op">{</span></span>
<span id="cb17-40"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-40" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Warning if you add an attribute like an ENV VAR you must do it via env.</span></span>
<span id="cb17-41"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-41" aria-hidden="true" tabindex="-1"></a>    <span class="kw">inherit</span> env<span class="op">;</span></span>
<span id="cb17-42"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-42" aria-hidden="true" tabindex="-1"></a>    <span class="co"># must contain buildInputs, nativeBuildInputs and shellHook</span></span>
<span id="cb17-43"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-43" aria-hidden="true" tabindex="-1"></a>    <span class="va">buildInputs</span> <span class="op">=</span> <span class="op">[</span> pkgs.coreutils</span>
<span id="cb17-44"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-44" aria-hidden="true" tabindex="-1"></a>                    pkgs.jdk11</span>
<span id="cb17-45"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-45" aria-hidden="true" tabindex="-1"></a>                    pkgs.lsof</span>
<span id="cb17-46"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-46" aria-hidden="true" tabindex="-1"></a>                    pkgs.plantuml</span>
<span id="cb17-47"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-47" aria-hidden="true" tabindex="-1"></a>                    pkgs.leiningen</span>
<span id="cb17-48"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-48" aria-hidden="true" tabindex="-1"></a>                  <span class="op">];</span></span>
<span id="cb17-49"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-49" aria-hidden="true" tabindex="-1"></a>    <span class="va">nativeBuildInputs</span> <span class="op">=</span> <span class="op">[</span></span>
<span id="cb17-50"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-50" aria-hidden="true" tabindex="-1"></a>      pkgs.zsh</span>
<span id="cb17-51"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-51" aria-hidden="true" tabindex="-1"></a>      pkgs.vim</span>
<span id="cb17-52"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-52" aria-hidden="true" tabindex="-1"></a>      pkgs.nixpkgs-fmt</span>
<span id="cb17-53"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-53" aria-hidden="true" tabindex="-1"></a>      pkgs.postgresql_11</span>
<span id="cb17-54"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-54" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-55"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-55" aria-hidden="true" tabindex="-1"></a>      <span class="co"># postgres-11 with postgis support</span></span>
<span id="cb17-56"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-56" aria-hidden="true" tabindex="-1"></a>      <span class="co"># (pkgs.postgresql_11.withPackages (p: [ p.postgis ]))</span></span>
<span id="cb17-57"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-57" aria-hidden="true" tabindex="-1"></a>    <span class="op">];</span></span>
<span id="cb17-58"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-58" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-59"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-59" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Post Shell Hook</span></span>
<span id="cb17-60"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-60" aria-hidden="true" tabindex="-1"></a>    <span class="va">shellHook</span> <span class="op">=</span> <span class="st">&#39;&#39;</span></span>
<span id="cb17-61"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-61" aria-hidden="true" tabindex="-1"></a><span class="st">      echo &quot;Using </span><span class="sc">${</span>pkgs.postgresql_11.name<span class="sc">}</span><span class="st">. port: </span><span class="sc">${</span>port<span class="sc">}</span><span class="st"> user: </span><span class="sc">${</span>pguser<span class="sc">}</span><span class="st"> pass: </span><span class="sc">${</span>pgpass<span class="sc">}</span><span class="st">&quot;</span></span>
<span id="cb17-62"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-63"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-63" aria-hidden="true" tabindex="-1"></a><span class="st">      # Setup: other env variables</span></span>
<span id="cb17-64"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-64" aria-hidden="true" tabindex="-1"></a><span class="st">      export PGHOST=&quot;$PGDATA&quot;</span></span>
<span id="cb17-65"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-65" aria-hidden="true" tabindex="-1"></a><span class="st">      # Setup: DB</span></span>
<span id="cb17-66"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-66" aria-hidden="true" tabindex="-1"></a><span class="st">      [ ! -d $PGDATA ] \</span></span>
<span id="cb17-67"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-67" aria-hidden="true" tabindex="-1"></a><span class="st">       &amp;&amp; pg_ctl initdb -o &quot;-U postgres&quot; \</span></span>
<span id="cb17-68"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-68" aria-hidden="true" tabindex="-1"></a><span class="st">       &amp;&amp; cat &quot;$postgresConf&quot; &gt;&gt; $PGDATA/postgresql.conf</span></span>
<span id="cb17-69"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-69" aria-hidden="true" tabindex="-1"></a><span class="st">      pg_ctl -o &quot;-p </span><span class="sc">${</span>port<span class="sc">}</span><span class="st"> -k $PGDATA&quot; start</span></span>
<span id="cb17-70"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-70" aria-hidden="true" tabindex="-1"></a><span class="st">      echo &quot;Creating DB and User&quot;</span></span>
<span id="cb17-71"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-71" aria-hidden="true" tabindex="-1"></a><span class="st">      psql -U postgres -p </span><span class="sc">${</span>port<span class="sc">}</span><span class="st"> -f $postgresInitScript</span></span>
<span id="cb17-72"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-72" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-73"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-73" aria-hidden="true" tabindex="-1"></a><span class="st">      function pgstop {</span></span>
<span id="cb17-74"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-74" aria-hidden="true" tabindex="-1"></a><span class="st">         echo &quot;Stopping and Cleaning up Postgres&quot;;</span></span>
<span id="cb17-75"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-75" aria-hidden="true" tabindex="-1"></a><span class="st">         pg_ctl stop &amp;&amp; rm -rf $PGDATA</span></span>
<span id="cb17-76"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-76" aria-hidden="true" tabindex="-1"></a><span class="st">      }</span></span>
<span id="cb17-77"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-77" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-78"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-78" aria-hidden="true" tabindex="-1"></a><span class="st">      alias pg=&quot;psql -p </span><span class="sc">${</span>port<span class="sc">}</span><span class="st"> -U postgres&quot;</span></span>
<span id="cb17-79"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-79" aria-hidden="true" tabindex="-1"></a><span class="st">      echo &quot;Send SQL commands with pg&quot;</span></span>
<span id="cb17-80"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-80" aria-hidden="true" tabindex="-1"></a><span class="st">      trap pgstop EXIT</span></span>
<span id="cb17-81"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-81" aria-hidden="true" tabindex="-1"></a><span class="st">      &#39;&#39;</span><span class="op">;</span></span>
<span id="cb17-82"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-82" aria-hidden="true" tabindex="-1"></a>    <span class="va">stop</span> <span class="op">=</span> <span class="st">&quot;pgstop&quot;</span><span class="op">;</span></span>
<span id="cb17-83"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb17-83" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span></code></pre></div><p>And to just launch Posgresql, there is also this file <code class="verbatim">./nix/pgshell.nix</code>, that simply contains</p><div class="sourceCode" id="cb18"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb18-1"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="va">pkgs</span> <span class="op">?</span> <span class="bu">import</span> <span class="op">(</span><span class="bu">fetchTarball</span> <span class="va">https</span><span class="op">://</span><span class="ss">github.com/NixOS/nixpkgs/archive/22.11.tar.gz</span><span class="op">)</span> <span class="op">{}</span> <span class="op">}</span>:</span>
<span id="cb18-2"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="va">pg</span> <span class="op">=</span> <span class="bu">import</span> <span class="ss">./pg.nix</span> <span class="op">{</span> <span class="kw">inherit</span> pkgs<span class="op">;</span> <span class="op">};</span></span>
<span id="cb18-3"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="kw">in</span> <span class="kw">with</span> pg<span class="op">;</span> pkgs.mkShell <span class="op">(</span> env <span class="op">//</span></span>
<span id="cb18-4"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb18-4" aria-hidden="true" tabindex="-1"></a>   <span class="op">{</span></span>
<span id="cb18-5"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb18-5" aria-hidden="true" tabindex="-1"></a>     <span class="va">buildInputs</span>       <span class="op">=</span> buildInputs<span class="op">;</span></span>
<span id="cb18-6"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb18-6" aria-hidden="true" tabindex="-1"></a>     <span class="va">nativeBuildInputs</span> <span class="op">=</span> nativeBuildInputs <span class="op">;</span></span>
<span id="cb18-7"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb18-7" aria-hidden="true" tabindex="-1"></a>     <span class="va">shellHook</span>         <span class="op">=</span> shellHook<span class="op">;</span></span>
<span id="cb18-8"><a href="https://yannesposito.com/posts/0024-replace-docker-compose-with-nix-shell/index.html#cb18-8" aria-hidden="true" tabindex="-1"></a>   <span class="op">})</span></span></code></pre></div></article></main>
]]></description>
</item>


<item>
<title>My first ChatGPT exchange</title>
<guid>https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html</guid>
<pubDate>Sun, 11 Dec 2022 00:00:00 +0100</pubDate>
<category>machine-learning</category>
<category>ai</category>
<category>gpt</category>
<description><![CDATA[
<main id="content"><article><h1 id="the-first-few-questions-for-chatgpt">The first few questions for
ChatGPT</h1><p>I finally opened an account to beta-test ChatGPT. I must say, I am
quite impressed. I asked it a few questions that I could have asked
during an interview. To my surprise, the answer given was, while wrong,
believable enough to kind of sneak the answer as right up to some
imprecision. Like if you squint enough, it could pass, but if you pay
attention it is clearly false.</p><p>So yes, mainly ChatGPT in this interaction was quite good at being
convincing. This is the same kind of impression you have from looking at
AI generated images. At first sight everything seems fine, but if you
start looking to the details, many things are off.</p><p>So here is the full interaction, I'll let the attentive reader find
the technical problems:</p><p><img src="https://yannesposito.com/logo.svg" alt="me" style="width:1.5em;filter:none"></img> Can
you write a clojure function that will reverse any string provided to
it.</p><blockquote><p><strong>ChatGPT</strong>: Sure, here's a possible implementation of a
function that reverses a string in Clojure:</p><div class="sourceCode" id="cb1"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb1-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> reverse-string </span>[s]</span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">apply</span> <span class="kw">str</span> (<span class="kw">reverse</span> (<span class="kw">seq</span> s))))</span></code></pre></div><p>This function takes a strings as input, converts it to a sequence of
characters using the <code>seq</code> function, reverses this sequence
using the reverse function, and then converts the resulting sequence
back to a string using the <code>apply</code> and <code>str</code>
functions.</p><p>Here's an example of how you can use this function:</p><div class="sourceCode" id="cb2"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb2-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a>user=&gt; (reverse-string <span class="st">&quot;hello world&quot;</span>)</span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;dirow olleh&quot;</span></span></code></pre></div><p>Note that this function only works for strings that contain
characters in the Unicode Basic Multilingual Plane (BMP), since the
<code>seq</code> function only works on strings that contain characters
in this range. If you need to reverse strings that contain characters
outside of the BMP, you can use the <code>string-seq</code> function
instead of <code>seq</code> to convert the string to a sequence of
characters.</p></blockquote><p><img src="https://yannesposito.com/logo.svg" alt="me" style="width:1.5em;filter:none"></img> How
would this function react if instead of string you provide an
integer?</p><blockquote><p><strong>ChatGPT:</strong> If you provide an integer as input to the
<code>reverse-string</code> function instead of a string, the function
will fail with a <code>ClassCastException</code> because the
<code>seq</code> function only works on strings, and you cannot convert
an integer to a string in a straightforward way in Clojure. The
<code>seq</code> function expects its input to be a string or a
collection, such as a vector or a list, and when you try to pass an
integer to it, it will try to convert the integer to a string by calling
the <code>str</code> function on it. However, since <code>str</code>
only works on strings and collections, and not on numbers, this will
result in a <code>ClassCastException</code>.</p><p>Here's an example of what will happen if you try to pass an integer
to the reverse-string function:</p><div class="sourceCode" id="cb3"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb3-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a>user=&gt; (reverse-string <span class="dv">12345</span>)</span>
<span id="cb3-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb3-2" aria-hidden="true" tabindex="-1"></a>ClassCastException clojure.lang. Numbers cannot be <span class="kw">cast</span> to</span>
<span id="cb3-3"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb3-3" aria-hidden="true" tabindex="-1"></a>clojure.lang. IFn clojure.core/seq (core.clj<span class="at">:136</span>)</span></code></pre></div><p>To avoid this error, you can either make sure that the input to the
<code>reverse-string</code> function is always a string, or you can
modify the function to handle other types of input, such as integers, by
first converting them to strings using the <code>str</code> function.
For example, you could write the <code>reverse-string</code> function
like this to handle both strings and integers as input:</p><div class="sourceCode" id="cb4"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb4-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb4-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> reverse-string </span>[s]</span>
<span id="cb4-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb4-2" aria-hidden="true" tabindex="-1"></a>(<span class="kw">let</span> [s (<span class="kw">if</span> (<span class="kw">string?</span> s) s (<span class="kw">str</span> s))]</span>
<span id="cb4-3"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb4-3" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">apply</span> <span class="kw">str</span> (<span class="kw">reverse</span> (<span class="kw">seq</span> s)))))</span></code></pre></div><p>With this version of the function, if you pass an integer as input,
the function will convert it to a string using the <code>str</code>
function before calling the <code>seq</code> function on it, so the
<code>seq</code> function will be able to work with the input. Here's an
example of how you can use this modified version of the
<code>reverse-string</code> function:</p><div class="sourceCode" id="cb5"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb5-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb5-1" aria-hidden="true" tabindex="-1"></a>user-&gt; (reverse-string <span class="st">&quot;hello world&quot;</span>)</span>
<span id="cb5-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;dlrow olleh&quot;</span></span>
<span id="cb5-3"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb5-4" aria-hidden="true" tabindex="-1"></a>user=&gt; (reverse-string <span class="dv">12345</span>)</span>
<span id="cb5-5"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;54321&quot;</span></span></code></pre></div><p>Note that the modified version of the function will also work for
other types of input that can be converted to strings using the
<code>str</code> function, such as floats and booleans.</p></blockquote><p><img src="https://yannesposito.com/logo.svg" alt="me" style="width:1.5em;filter:none"></img> How
would you use the concept of the Maybe monad in Haskell in this clojure
function?</p><blockquote><p><strong>ChatGPT</strong>: The Maybe monad in Haskell is a way to
represent computations that may or may not have & p a result, and it
provides a way to compose these computations in a safe and predictable
way. In Clojure, the Maybe monad is usually implemented as a
<code>Option</code> type, which has two possible values:
<code>Some</code> and <code>None</code></p><p>In the context of the <code>reverse-string</code> function, you could
use the <code>Option</code> type to represent the possibility that the
input to the function might not be a string, in which case the function
would return <code>None</code> instead of a reversed string. Here's an
example of how you could use the <code>Option</code> type to modify the
<code>reverse-string</code> function to handle the case where the input
is not a string:</p><div class="sourceCode" id="cb6"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb6-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb6-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> reverse-string </span>[s]</span>
<span id="cb6-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb6-2" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">if</span> (<span class="kw">string?</span> s)</span>
<span id="cb6-3"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb6-3" aria-hidden="true" tabindex="-1"></a>    (Some (<span class="kw">apply</span> <span class="kw">str</span> (<span class="kw">reverse</span> (<span class="kw">seq</span> s))))</span>
<span id="cb6-4"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb6-4" aria-hidden="true" tabindex="-1"></a>    None))</span></code></pre></div><p>With this version of the <code>reverse-string</code> function, if the
input is a string, the function will return a <code>Some</code> value
containing the reversed string, and if the input is not a string, the
function will return a <code>None</code> value. This allows the caller
of the function to handle the case where the input is not a string in a
predictable and safe way.</p><p>Here's an example of how you can use this modified version of the
<code>reverse-string</code> function:</p><div class="sourceCode" id="cb7"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb7-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb7-1" aria-hidden="true" tabindex="-1"></a>user=&gt; (reverse-string <span class="st">&quot;hello world&quot;</span>)</span>
<span id="cb7-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb7-3" aria-hidden="true" tabindex="-1"></a>Some <span class="st">&quot;dlrow olleh&quot;</span></span>
<span id="cb7-4"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb7-4" aria-hidden="true" tabindex="-1"></a>user=&gt; (reverse-string <span class="dv">12345</span>)</span>
<span id="cb7-5"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb7-5" aria-hidden="true" tabindex="-1"></a>None</span></code></pre></div><p>Note that the Option type in Clojure does not provide the same
syntactic sugar for composing computations as the Maybe monad in
Haskell, so you cannot use the <code>>>=</code> operator to chain
calls to the <code>reverse-string</code> function in the same way you
can in Haskell. However, you can still use the <code>Option</code> type
to represent computations that may or may not have a result, and you can
use the <code>match</code> or <code>case</code> forms to pattern match
on the <code>Option</code> values and handle the different cases in a
predictable and safe way.</p></blockquote><p><img src="https://yannesposito.com/logo.svg" alt="me" style="width:1.5em;filter:none"></img> How
would you implement Option in clojure?</p><blockquote><p><strong>ChatGPT</strong>: In Clojure, the <code>Option</code> type is
typically implemented as a sealed protocol, which is a type of
polymorphic type that defines a set of concrete types that implement the
protocol.</p><p>The <code>Option</code> protocol typically defines two concrete
types: <code>Some</code>, which represents a computation that has a
result, and <code>None</code>, which represents a computation that does
not have a result.</p><p>Here's an example of how you could define the <code>Option</code>
protocol and the <code>Some</code> and <code>None</code> types in
Clojure:</p><div class="sourceCode" id="cb8"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb8-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defprotocol</span><span class="fu"> Option</span></span>
<span id="cb8-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-2" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">map</span> [o f])</span>
<span id="cb8-3"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-3" aria-hidden="true" tabindex="-1"></a>  (flat-map [o f]))</span>
<span id="cb8-4"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-5" aria-hidden="true" tabindex="-1"></a>(<span class="bu">deftype</span><span class="fu"> Some </span>[x]</span>
<span id="cb8-6"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-6" aria-hidden="true" tabindex="-1"></a>  Option</span>
<span id="cb8-7"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-7" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">map</span> [o f] (Some (f x)))</span>
<span id="cb8-8"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-8" aria-hidden="true" tabindex="-1"></a>  (flat-map [o f] (f x)))</span>
<span id="cb8-9"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-10"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-10" aria-hidden="true" tabindex="-1"></a>(<span class="bu">deftype</span><span class="fu"> None </span>[])</span>
<span id="cb8-11"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-11" aria-hidden="true" tabindex="-1"></a>  Option</span>
<span id="cb8-12"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-12" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">map</span> [o f] None)</span>
<span id="cb8-13"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb8-13" aria-hidden="true" tabindex="-1"></a>  (flat-map [o f] None))</span></code></pre></div><p>The <code>Option</code> protocol defines two methods:
<code>map</code> and <code>flat-map</code> The <code>map</code> method
takes a function <code>f</code> as input and applies it to the value
contained in the <code>Some</code> type, <code>f</code> it exists, or
returns a <code>None</code> value <code>f</code> the <code>Option</code>
type is <code>None</code> The <code>flat-map</code> method is similar to
<code>map</code> but it allows the function <code>f</code> to return an
<code>Option</code> value, which is then flattened into the outer
<code>Option</code> value.</p><p>The <code>Some</code> type implements the <code>Option</code>
protocol by providing concrete implementations <code>f</code> the map
and <code>flat-map</code> methods. The 'map method applies the function
<code>f</code> to the value <code>x</code> contained in the
<code>Some</code> type, and returns a new <code>Some</code> value
containing the result <code>f</code> the function. The
<code>flat-map</code> method applies the function <code>f</code> to the
value <code>x</code> contained in the Some type, and then flattens the
resulting <code>Option</code> value into the outer <code>Option</code>
value.</p><p>The <code>None</code> type also implements the Option protocol by
providing concrete implementations of the <code>map</code> and
<code>flat-map</code> methods. The <code>map</code> and
<code>flat-map</code> methods simply return a <code>None</code> value in
this case, since there is no value to apply the function to.</p><p>Here's an example of how you can use the <code>Option</code> protocol
and the <code>Some</code> and <code>None</code> types:</p><div class="sourceCode" id="cb9"><pre class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb9-1"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-1" aria-hidden="true" tabindex="-1"></a>user=&gt; (<span class="bu">def</span><span class="fu"> some-value </span>(Some <span class="dv">12345</span>))</span>
<span id="cb9-2"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-2" aria-hidden="true" tabindex="-1"></a>#&#39; user/some-value</span>
<span id="cb9-3"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-4"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-4" aria-hidden="true" tabindex="-1"></a>user=&gt; (<span class="kw">map</span> some-value <span class="kw">inc</span>)</span>
<span id="cb9-5"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-5" aria-hidden="true" tabindex="-1"></a>Some <span class="dv">12346</span></span>
<span id="cb9-6"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-7"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-7" aria-hidden="true" tabindex="-1"></a>user=&gt; (flat-map some-value (<span class="kw">fn</span> [x] (Some (<span class="kw">*</span> × <span class="dv">2</span>))))</span>
<span id="cb9-8"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-8" aria-hidden="true" tabindex="-1"></a>Some <span class="dv">24690</span></span>
<span id="cb9-9"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-10"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-10" aria-hidden="true" tabindex="-1"></a>user=&gt; (<span class="kw">map</span> some-value (<span class="kw">comp</span> (<span class="kw">partial</span> <span class="kw">*</span> <span class="dv">2</span>) <span class="kw">inc</span>))</span>
<span id="cb9-11"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-11" aria-hidden="true" tabindex="-1"></a>Some <span class="dv">24690</span></span>
<span id="cb9-12"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-13"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-13" aria-hidden="true" tabindex="-1"></a>user=&gt; (<span class="bu">def</span><span class="fu"> none-value </span>None)</span>
<span id="cb9-14"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="va">#&#39;user/none-value</span></span>
<span id="cb9-15"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-16"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-16" aria-hidden="true" tabindex="-1"></a>user=&gt; (<span class="kw">map</span> none-value <span class="kw">inc</span>)</span>
<span id="cb9-17"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-17" aria-hidden="true" tabindex="-1"></a>None</span>
<span id="cb9-18"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-19"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-19" aria-hidden="true" tabindex="-1"></a>user=&gt; (flat-map none-value (<span class="kw">fn</span> [x] (Some (<span class="kw">*</span> × <span class="dv">2</span>))))</span>
<span id="cb9-20"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-20" aria-hidden="true" tabindex="-1"></a>None</span>
<span id="cb9-21"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-22"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-22" aria-hidden="true" tabindex="-1"></a>user=&gt; (<span class="kw">map</span> none-value (<span class="kw">comp</span> (<span class="kw">partial</span> <span class="kw">*</span> <span class="dv">2</span>) <span class="kw">inc</span>))</span>
<span id="cb9-23"><a href="https://yannesposito.com/posts/0023-my-first-chatgpt-exchange/index.html#cb9-23" aria-hidden="true" tabindex="-1"></a>None</span></code></pre></div><p>In this example, the <code>some-value</code> variable is defined as a
<code>Some</code> value containing the integer 12345, and the
<code>none-value</code> variable is defined as a <code>None</code>
value. The <code>map</code> and <code>flat-map</code> functions are then
used to apply different functions to the values contained in the
<code>Option</code> types, and the…</p></blockquote></article></main>
]]></description>
</item>


<item>
<title>Org agenda on your startup page</title>
<guid>https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html</guid>
<pubDate>Thu, 05 May 2022 00:00:00 +0200</pubDate>
<category>blog</category>
<category>static</category>
<description><![CDATA[
<main id="content"><article><h1 id="d88a1458-07bb-4c73-86c3-9f09b3d6a1ad" id="your-org-agenda-at-your-homepage">Your org-agenda at your
homepage</h1><p>During most of the day my emacs is open. But I sometime forget to
look at my org-agenda. This is why I wanted to find another way to be
exposed to it.</p><p>And one thing I am mostly exposed to is my personal start page. This
is just the default page I see when I open my browser.</p><p>Here is the end result:</p><figure><img src="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/agenda-html.png.webp" title="agenda-html" alt="The result inside my start page"></img><figcaption aria-hidden="true">The result inside my start
page</figcaption></figure><p>My start page is named <em>mothership</em>. And I just put the
org-agenda at the top of the page inside an iframe. I have a service
that start a server on localhost and I configured my browser to use it
as startup page. That's it.</p><p>So now, here is how to sync my org-agenda on this start page.</p><p>In my <code>config.el</code>:</p><pre class="elisp"><code>(setq y/mothership &quot;~/dev/mothership/&quot;)
(setq org-agenda-custom-commands
      `((&quot;g&quot; &quot;Plan Today&quot;
         ((agenda &quot;&quot; ((org-agenda-span &#39;day))))
         nil
         ( ,(concat y/mothership &quot;/agenda.html&quot;) ))))
</code></pre><p>This provide a custom org agenda command and link that custom command
to the export file <code class="verbatim">~/dev/mothership/agenda.html</code>.</p><p>And a shell script:</p><div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env bash</span></span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ex">emacs</span> <span class="at">--batch</span> <span class="dt">\</span></span>
<span id="cb2-3"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb2-3" aria-hidden="true" tabindex="-1"></a>  <span class="at">--load</span> <span class="st">&quot;</span><span class="va">$HOME</span><span class="st">/.emacs.d/init.el&quot;</span> <span class="dt">\</span></span>
<span id="cb2-4"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb2-4" aria-hidden="true" tabindex="-1"></a>  <span class="at">--eval</span> <span class="st">&#39;(org-batch-store-agenda-views)&#39;</span> <span class="dt">\</span></span>
<span id="cb2-5"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb2-5" aria-hidden="true" tabindex="-1"></a>  <span class="at">--kill</span></span></code></pre></div><div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">*/5</span> <span class="pp">*</span> <span class="pp">*</span> <span class="pp">*</span> <span class="pp">*</span> /Users/esposito/dev/mothership/export-agenda.sh</span></code></pre></div><p>And finally in my start-page html I just need to add an iframe like
so:</p><div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;iframe</span> <span class="er">id</span><span class="ot">=</span><span class="st">&quot;agenda&quot;</span> <span class="er">src</span><span class="ot">=</span><span class="st">&quot;agenda.html&quot;</span><span class="kw">&gt;&lt;/iframe&gt;</span></span></code></pre></div><p>But as I also want to be able to toggle the agenda, and auto-resize
the iframe. So I added a bit of js code:</p><div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;div</span> <span class="er">id</span><span class="ot">=</span><span class="st">&quot;agendatoggle&quot;</span><span class="kw">&gt;</span>[-]<span class="kw">&lt;/div&gt;&lt;br/&gt;</span></span>
<span id="cb5-2"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;script&gt;</span></span>
<span id="cb5-3"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">resizeIframe</span>(iframe) {</span>
<span id="cb5-4"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-4" aria-hidden="true" tabindex="-1"></a>  iframe<span class="op">.</span><span class="at">height</span> <span class="op">=</span> ( iframe<span class="op">.</span><span class="at">contentWindow</span><span class="op">.</span><span class="at">document</span><span class="op">.</span><span class="at">body</span><span class="op">.</span><span class="at">scrollHeight</span> <span class="op">+</span> <span class="dv">30</span>) <span class="op">+</span> <span class="st">&quot;px&quot;</span><span class="op">;</span></span>
<span id="cb5-5"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-5" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb5-6"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;/script&gt;</span></span>
<span id="cb5-7"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;iframe</span> <span class="er">id</span><span class="ot">=</span><span class="st">&quot;agenda&quot;</span> <span class="er">src</span><span class="ot">=</span><span class="st">&quot;agenda.html&quot;</span> <span class="er">onload</span><span class="ot">=</span><span class="st">&quot;resizeIframe(this)&quot;</span><span class="kw">&gt;&lt;/iframe&gt;</span></span>
<span id="cb5-8"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;/div&gt;</span></span>
<span id="cb5-9"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;script&gt;</span></span>
<span id="cb5-10"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-11"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">hideAgenda</span> () {</span>
<span id="cb5-12"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-12" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> agenda <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">getElementById</span>(<span class="st">&quot;agenda&quot;</span>)<span class="op">;</span></span>
<span id="cb5-13"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-13" aria-hidden="true" tabindex="-1"></a>  agenda<span class="op">.</span><span class="at">style</span><span class="op">.</span><span class="at">display</span> <span class="op">=</span> <span class="st">&#39;none&#39;</span><span class="op">;</span></span>
<span id="cb5-14"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-14" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> at <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">getElementById</span>(<span class="st">&quot;agendatoggle&quot;</span>)<span class="op">;</span></span>
<span id="cb5-15"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-15" aria-hidden="true" tabindex="-1"></a>  at<span class="op">.</span><span class="at">innerHTML</span><span class="op">=</span><span class="st">&quot;agenda [+]&quot;</span><span class="op">;</span></span>
<span id="cb5-16"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-16" aria-hidden="true" tabindex="-1"></a>  at<span class="op">.</span><span class="at">onclick</span> <span class="op">=</span> showAgenda<span class="op">;</span></span>
<span id="cb5-17"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-17" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb5-18"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-19"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-19" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">showAgenda</span> () {</span>
<span id="cb5-20"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-20" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> agenda <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">getElementById</span>(<span class="st">&quot;agenda&quot;</span>)<span class="op">;</span></span>
<span id="cb5-21"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-21" aria-hidden="true" tabindex="-1"></a>  agenda<span class="op">.</span><span class="at">style</span><span class="op">.</span><span class="at">display</span> <span class="op">=</span> <span class="st">&#39;block&#39;</span><span class="op">;</span></span>
<span id="cb5-22"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-22" aria-hidden="true" tabindex="-1"></a>  <span class="fu">resizeIframe</span>(agenda)<span class="op">;</span></span>
<span id="cb5-23"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-23" aria-hidden="true" tabindex="-1"></a>  <span class="co">// setInterval(function(){ ; }, 1000);</span></span>
<span id="cb5-24"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-24" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> at <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">getElementById</span>(<span class="st">&quot;agendatoggle&quot;</span>)<span class="op">;</span></span>
<span id="cb5-25"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-25" aria-hidden="true" tabindex="-1"></a>  at<span class="op">.</span><span class="at">innerHTML</span><span class="op">=</span><span class="st">&quot;agenda [-]&quot;</span><span class="op">;</span></span>
<span id="cb5-26"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-26" aria-hidden="true" tabindex="-1"></a>  at<span class="op">.</span><span class="at">onclick</span> <span class="op">=</span> hideAgenda<span class="op">;</span></span>
<span id="cb5-27"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-27" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb5-28"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-29"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-29" aria-hidden="true" tabindex="-1"></a><span class="fu">showAgenda</span>()<span class="op">;</span></span>
<span id="cb5-30"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb5-30" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;/script&gt;</span></span></code></pre></div><p>And that's it. That's a neat trick, and so I'm glad to put a small
post about it.</p><h2 id="bonuses">Bonuses</h2><h3 id="auto-resize-iframe">auto-resize iframe</h3><p>In order to auto-resize the iframe you must have a non <code class="verbatim">file:///</code> URL in the browser. So you must serve
your start page. After a lot of different way to serve my pages, I
finally use <code>lighttpd</code> inside a <code>nix-shell</code>.</p><p>So I added the following to my startpage code:</p><p>A <a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/lighttpd.conf">./lighttpd.conf</a> file</p><pre class="conf" data-tangle="lighttpd.conf"><code>server.bind = &quot;127.0.0.1&quot;
server.port = 31337
server.document-root = var.CWD

index-file.names = ( &quot;index.html&quot; )

mimetype.assign = (
  &quot;.css&quot;        =&gt;  &quot;text/css&quot;,
  &quot;.gif&quot;        =&gt;  &quot;image/gif&quot;,
  &quot;.htm&quot;        =&gt;  &quot;text/html&quot;,
  &quot;.html&quot;       =&gt;  &quot;text/html&quot;,
  &quot;.jpeg&quot;       =&gt;  &quot;image/jpeg&quot;,
  &quot;.jpg&quot;        =&gt;  &quot;image/jpeg&quot;,
  &quot;.js&quot;         =&gt;  &quot;text/javascript&quot;,
  &quot;.png&quot;        =&gt;  &quot;image/png&quot;,
  &quot;.swf&quot;        =&gt;  &quot;application/x-shockwave-flash&quot;,
  &quot;.txt&quot;        =&gt;  &quot;text/plain&quot;,
  &quot;.gmi&quot;        =&gt;  &quot;text/plain&quot;,
  &quot;.svg&quot;        =&gt;  &quot;image/svg+xml&quot;,
  &quot;.svgz&quot;       =&gt;  &quot;image/svg+xml&quot;
)

# Making sure file uploads above 64k always work when using IE or Safari
# For more information, see http://trac.lighttpd.net/trac/ticket/360
$HTTP[&quot;useragent&quot;] =~ &quot;^(.*MSIE.*)|(.*AppleWebKit.*)$&quot; {
  server.max-keep-alive-requests = 0
}
</code></pre><p>With a</p><div class="sourceCode" id="cb7" data-tangle="serve.sh"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co">#! /user/bin/env nix-shell</span></span>
<span id="cb7-2"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="co">#! nix-shell shell.nix -i bash</span></span>
<span id="cb7-3"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="va">webdir</span><span class="op">=</span><span class="st">&quot;_site&quot;</span></span>
<span id="cb7-4"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="va">port</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="fu">grep</span> server.port ./lighttpd.conf<span class="kw">|</span><span class="fu">sed</span> <span class="st">&#39;s/[^0-9]*//&#39;</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb7-5"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;Serving: </span><span class="va">$webdir</span><span class="st"> on http://localhost:</span><span class="va">$port</span><span class="st">&quot;</span> <span class="kw">&amp;&amp;</span> <span class="dt">\</span></span>
<span id="cb7-6"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="ex">lighttpd</span> <span class="at">-f</span> ./lighttpd.conf <span class="at">-D</span></span></code></pre></div><p>I have a frozen nixpkgs dependencies via <code class="verbatim">niv</code>. But you could probably simply replace the
line by:</p><pre><code>#! nix-shell -p lighttpd -i bash
</code></pre><p>And it should work fine.</p><h3 id="start-your-server-at-startup-on-macos">Start your server at
startup on macOS</h3><p>Last but not least, starting this start page server when I login.</p><p>So for that you should demonize with launchd.</p><p>I created a <a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/y.mothership.plist">./y.mothership.plist</a>
file to put in <code class="verbatim">~/Library/LauchAgents/</code>:</p><div class="sourceCode" id="cb9" data-tangle="y.mothership.plist"><pre class="sourceCode xml"><code class="sourceCode xml"><span id="cb9-1"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&quot;1.0&quot;</span><span class="ot"> encoding=</span><span class="st">&quot;UTF-8&quot;</span><span class="fu">?&gt;</span></span>
<span id="cb9-2"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;!DOCTYPE</span> <span class="dt">plist</span> PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;<span class="dt">&gt;</span></span>
<span id="cb9-3"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-3" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">plist</span><span class="ot"> version=</span><span class="st">&quot;1.0&quot;</span>&gt;</span>
<span id="cb9-4"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-4" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">dict</span>&gt;</span>
<span id="cb9-5"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-5" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">key</span>&gt;Label&lt;/<span class="kw">key</span>&gt;</span>
<span id="cb9-6"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-6" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">string</span>&gt;mothership&lt;/<span class="kw">string</span>&gt;</span>
<span id="cb9-7"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-7" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">key</span>&gt;ProgramArguments&lt;/<span class="kw">key</span>&gt;</span>
<span id="cb9-8"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-8" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">array</span>&gt;</span>
<span id="cb9-9"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-9" aria-hidden="true" tabindex="-1"></a>        &lt;<span class="kw">string</span>&gt;/bin/zsh&lt;/<span class="kw">string</span>&gt;</span>
<span id="cb9-10"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-10" aria-hidden="true" tabindex="-1"></a>        &lt;<span class="kw">string</span>&gt;-c&lt;/<span class="kw">string</span>&gt;</span>
<span id="cb9-11"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-11" aria-hidden="true" tabindex="-1"></a>        &lt;<span class="kw">string</span>&gt;$HOME/y/mothership/serve.sh&lt;/<span class="kw">string</span>&gt;</span>
<span id="cb9-12"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-12" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">array</span>&gt;</span>
<span id="cb9-13"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-13" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">key</span>&gt;StandardOutPath&lt;/<span class="kw">key</span>&gt;</span>
<span id="cb9-14"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-14" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">string</span>&gt;/var/log/mothership.log&lt;/<span class="kw">string</span>&gt;</span>
<span id="cb9-15"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-15" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">key</span>&gt;StandardErrorPath&lt;/<span class="kw">key</span>&gt;</span>
<span id="cb9-16"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-16" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">string</span>&gt;/var/log/mothership.log&lt;/<span class="kw">string</span>&gt;</span>
<span id="cb9-17"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-17" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">dict</span>&gt;</span>
<span id="cb9-18"><a href="https://yannesposito.com/posts/0022-org-agenda-on-your-startup-page/index.html#cb9-18" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">plist</span>&gt;</span></code></pre></div><p>Then to ensure that the executable <code>nix-shell</code> is present
in the PATH for the demon, I ran:</p><pre><code>launchctl config user path &quot;$PATH&quot;
</code></pre><p>This will affect the path of all users, but as I am the only user on
my computer this is fine for me.</p><p>Then:</p><pre><code>launchctl load ~/Library/LaunchAgents/y.mothership.plist
launchctl start mothership
</code></pre><p>And that's about it.</p></article></main>
]]></description>
</item>


<item>
<title>My personal environment sync</title>
<guid>https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html</guid>
<pubDate>Sat, 30 Oct 2021 00:00:00 +0200</pubDate>
<category>programming</category>
<description><![CDATA[
<main id="content"><article><p>I have a quite specific system that I improved along the years to
manage my local environment. Think about, binaries I expect to have in
my shell, as well as configuration files for various utilities, and a
few personal scripts.</p><p>The notion of what is exactly my local environment is not perfectly
defined. I expect every of my computers to behave slightly differently.
Some are for work-only, some personal use only.</p><p>For the things I want everywhere, I have a peculiar personal
system.</p><p>I use a personal script that depends on <a href="https://yadm.io">yadm</a> and <a href="https://github.com/nix-community/home-manager">home-manager</a>.</p><p>My script try to check if some files where updated and react
accordingly:</p><ol><li>I download the dot-files changes via <code class="verbatim">yadm</code>.</li><li>If my home-manager files changes, it will run
<code>home-manager switch</code> ; if it fails, try to update nix
channels then try again.</li><li>If my doom emacs packages changed, it will run
<code>doom sync</code></li><li>If the script itself changed, it re-run the script after updating
itself.</li></ol><p>If the script detect that I changed my emacs configuration, it runs
<code>doom
sync</code> or <code>doom sync -u</code>.</p><p>Here it is:</p><div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">### logs fn helpers</span></span>
<span id="cb1-4"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co">## colors for tput</span></span>
<span id="cb1-6"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="co"># black=0</span></span>
<span id="cb1-7"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="va">red</span><span class="op">=</span>1</span>
<span id="cb1-8"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="va">green</span><span class="op">=</span>2</span>
<span id="cb1-9"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="va">yellow</span><span class="op">=</span>3</span>
<span id="cb1-10"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="va">blue</span><span class="op">=</span>4</span>
<span id="cb1-11"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="co"># magenta=5</span></span>
<span id="cb1-12"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co"># cyan=6</span></span>
<span id="cb1-13"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="co"># white=7</span></span>
<span id="cb1-14"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="fu">highpr()</span> <span class="kw">{</span></span>
<span id="cb1-15"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-15" aria-hidden="true" tabindex="-1"></a>    <span class="bu">printf</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">tput</span> setaf <span class="va">$green)</span><span class="st">→</span><span class="va">$(</span><span class="fu">tput</span> sgr0<span class="va">)</span><span class="st"> </span><span class="va">$(</span><span class="fu">tput</span> bold<span class="va">)</span><span class="st">%-60s</span><span class="va">$(</span><span class="fu">tput</span> sgr0<span class="va">)</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$*</span><span class="st">&quot;</span></span>
<span id="cb1-16"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb1-17"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="fu">ok()</span> <span class="kw">{</span></span>
<span id="cb1-18"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-18" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">txt</span><span class="op">=</span><span class="st">&quot;OK&quot;</span></span>
<span id="cb1-19"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-19" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="at">-e</span> <span class="st">&quot; [</span><span class="va">$(</span><span class="fu">tput</span> bold<span class="va">)$(</span><span class="fu">tput</span> setaf <span class="va">$green)${txt}$(</span><span class="fu">tput</span> sgr0<span class="va">)</span><span class="st">]&quot;</span> <span class="op">&gt;&amp;</span><span class="dv">2</span></span>
<span id="cb1-20"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb1-21"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="fu">info()</span> <span class="kw">{</span></span>
<span id="cb1-22"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-22" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="at">-e</span> <span class="st">&quot; [</span><span class="va">$(</span><span class="fu">tput</span> bold<span class="va">)$(</span><span class="fu">tput</span> setaf <span class="va">$blue)$*$(</span><span class="fu">tput</span> sgr0<span class="va">)</span><span class="st">]&quot;</span> <span class="op">&gt;&amp;</span><span class="dv">2</span></span>
<span id="cb1-23"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb1-24"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-24" aria-hidden="true" tabindex="-1"></a><span class="fu">warn()</span> <span class="kw">{</span></span>
<span id="cb1-25"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-25" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="at">-e</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">tput</span> bold<span class="va">)$(</span><span class="fu">tput</span> setaf <span class="va">$yellow)$*$(</span><span class="fu">tput</span> sgr0<span class="va">)</span><span class="st">&quot;</span> <span class="op">&gt;&amp;</span><span class="dv">2</span></span>
<span id="cb1-26"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-26" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb1-27"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-27" aria-hidden="true" tabindex="-1"></a><span class="fu">err()</span> <span class="kw">{</span></span>
<span id="cb1-28"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-28" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="at">-e</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">tput</span> bold<span class="va">)$(</span><span class="fu">tput</span> setaf <span class="va">$red)$*$(</span><span class="fu">tput</span> sgr0<span class="va">)</span><span class="st">&quot;</span> <span class="op">&gt;&amp;</span><span class="dv">2</span></span>
<span id="cb1-29"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-29" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb1-30"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-30" aria-hidden="true" tabindex="-1"></a><span class="fu">fail()</span> <span class="kw">{</span></span>
<span id="cb1-31"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-31" aria-hidden="true" tabindex="-1"></a>    <span class="ex">err</span> <span class="at">-e</span> <span class="st">&quot;\n[ERR] </span><span class="va">$*</span><span class="st">&quot;</span></span>
<span id="cb1-32"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-32" aria-hidden="true" tabindex="-1"></a>    <span class="bu">exit</span> 1</span>
<span id="cb1-33"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-33" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb1-34"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-35"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-35" aria-hidden="true" tabindex="-1"></a><span class="ex">highpr</span> <span class="st">&quot;check nix&quot;</span></span>
<span id="cb1-36"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-36" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="ot">! </span><span class="bu">[</span> <span class="ot">-x</span> <span class="st">&quot;</span><span class="va">$(</span><span class="bu">command</span> <span class="at">-v</span> nix<span class="va">)</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb1-37"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-37" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span></span>
<span id="cb1-38"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-38" aria-hidden="true" tabindex="-1"></a>    <span class="ex">err</span> <span class="st">&quot;nix does not seem to be installed.&quot;</span></span>
<span id="cb1-39"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-39" aria-hidden="true" tabindex="-1"></a>    <span class="ex">err</span> <span class="st">&quot;Install it from: https://nixos.org/nix/&quot;</span></span>
<span id="cb1-40"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-40" aria-hidden="true" tabindex="-1"></a>    <span class="bu">exit</span> 1</span>
<span id="cb1-41"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-41" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb1-42"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-42" aria-hidden="true" tabindex="-1"></a><span class="ex">ok</span></span>
<span id="cb1-43"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-44"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-44" aria-hidden="true" tabindex="-1"></a><span class="ex">highpr</span> <span class="st">&quot;yadm fetch&quot;</span></span>
<span id="cb1-45"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-45" aria-hidden="true" tabindex="-1"></a><span class="ex">yadm</span> fetch <span class="at">--quiet</span>  <span class="kw">||</span> <span class="ex">fail</span> <span class="st">&quot;yadm fetch failed&quot;</span></span>
<span id="cb1-46"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-46" aria-hidden="true" tabindex="-1"></a><span class="ex">ok</span></span>
<span id="cb1-47"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-47" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-48"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-48" aria-hidden="true" tabindex="-1"></a><span class="co"># check the hash of a few files before doing yadm pull</span></span>
<span id="cb1-49"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-49" aria-hidden="true" tabindex="-1"></a><span class="va">OLD_SYNC_ENV_ID</span><span class="op">=</span><span class="va">$(</span><span class="ex">yadm</span> rev-parse HEAD:bin/sync-env.sh<span class="va">)</span></span>
<span id="cb1-50"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-50" aria-hidden="true" tabindex="-1"></a><span class="va">OLD_HOME_MANAGER_ID</span><span class="op">=</span><span class="va">$(</span><span class="ex">yadm</span> rev-parse HEAD:.config/nixpkgs/home.nix<span class="va">)</span></span>
<span id="cb1-51"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-51" aria-hidden="true" tabindex="-1"></a><span class="va">OLD_DOOM_PACKAGES</span><span class="op">=</span><span class="va">$(</span><span class="ex">yadm</span> rev-parse HEAD:.doom.d/packages.el<span class="va">)</span></span>
<span id="cb1-52"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-52" aria-hidden="true" tabindex="-1"></a><span class="va">OLD_DOOM_INIT</span><span class="op">=</span><span class="va">$(</span><span class="ex">yadm</span> rev-parse HEAD:.doom.d/init.el<span class="va">)</span></span>
<span id="cb1-53"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-54"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-54" aria-hidden="true" tabindex="-1"></a><span class="ex">highpr</span> <span class="st">&quot;yadm pull&quot;</span></span>
<span id="cb1-55"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-55" aria-hidden="true" tabindex="-1"></a><span class="ex">yadm</span> pull <span class="at">--quiet</span> <span class="kw">||</span> <span class="ex">fail</span> <span class="st">&quot;yadm pull failed&quot;</span></span>
<span id="cb1-56"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-56" aria-hidden="true" tabindex="-1"></a><span class="ex">ok</span></span>
<span id="cb1-57"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-57" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-58"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-58" aria-hidden="true" tabindex="-1"></a><span class="co"># check the hash of a few files after doing yadm pull</span></span>
<span id="cb1-59"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-59" aria-hidden="true" tabindex="-1"></a><span class="va">NEW_SYNC_ENV_ID</span><span class="op">=</span><span class="va">$(</span><span class="ex">yadm</span> rev-parse HEAD:bin/sync-env.sh<span class="va">)</span></span>
<span id="cb1-60"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-60" aria-hidden="true" tabindex="-1"></a><span class="va">NEW_HOME_MANAGER_ID</span><span class="op">=</span><span class="va">$(</span><span class="ex">yadm</span> rev-parse HEAD:.config/nixpkgs/home.nix<span class="va">)</span></span>
<span id="cb1-61"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-61" aria-hidden="true" tabindex="-1"></a><span class="va">NEW_DOOM_PACKAGES</span><span class="op">=</span><span class="va">$(</span><span class="ex">yadm</span> rev-parse HEAD:.doom.d/packages.el<span class="va">)</span></span>
<span id="cb1-62"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-62" aria-hidden="true" tabindex="-1"></a><span class="va">NEW_DOOM_INIT</span><span class="op">=</span><span class="va">$(</span><span class="ex">yadm</span> rev-parse HEAD:.doom.d/init.el<span class="va">)</span></span>
<span id="cb1-63"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-63" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-64"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-64" aria-hidden="true" tabindex="-1"></a><span class="ex">highpr</span> <span class="st">&quot;check sync-env diff&quot;</span></span>
<span id="cb1-65"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-65" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="ot">! </span><span class="bu">[</span> <span class="st">&quot;</span><span class="va">$OLD_SYNC_ENV_ID</span><span class="st">&quot;</span> <span class="ot">=</span> <span class="st">&quot;</span><span class="va">$NEW_SYNC_ENV_ID</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb1-66"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-66" aria-hidden="true" tabindex="-1"></a>    <span class="ex">warn</span> <span class="st">&quot; changed&quot;</span></span>
<span id="cb1-67"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-67" aria-hidden="true" tabindex="-1"></a>    <span class="ex">warn</span> <span class="st">&quot; Starting ~/bin/sync-env.sh again&quot;</span></span>
<span id="cb1-68"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-68" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span></span>
<span id="cb1-69"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-69" aria-hidden="true" tabindex="-1"></a>    <span class="ex">~/bin/sync-env.sh</span></span>
<span id="cb1-70"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-70" aria-hidden="true" tabindex="-1"></a>    <span class="bu">exit</span> <span class="va">$?</span></span>
<span id="cb1-71"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-71" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb1-72"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-72" aria-hidden="true" tabindex="-1"></a><span class="ex">ok</span></span>
<span id="cb1-73"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-73" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-74"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-74" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="bu">[</span> <span class="ot">-f</span> <span class="st">&quot;</span><span class="va">$HOME</span><span class="st">/.yadm/files.gpg&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb1-75"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-75" aria-hidden="true" tabindex="-1"></a>  <span class="ex">highpr</span> <span class="st">&quot;yadm decrypt&quot;</span></span>
<span id="cb1-76"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-76" aria-hidden="true" tabindex="-1"></a>  <span class="ex">yadm</span> decrypt <span class="kw">||</span> <span class="ex">fail</span> <span class="st">&quot;yadm decrypt failed&quot;</span></span>
<span id="cb1-77"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-77" aria-hidden="true" tabindex="-1"></a>  <span class="ex">ok</span></span>
<span id="cb1-78"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-78" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb1-79"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-79" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-80"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-80" aria-hidden="true" tabindex="-1"></a><span class="ex">highpr</span> <span class="st">&quot;home-manager&quot;</span></span>
<span id="cb1-81"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-81" aria-hidden="true" tabindex="-1"></a><span class="va">USERNAME_NIX_FILE</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$HOME</span><span class="st">/.config/nixpkgs/username.nix&quot;</span></span>
<span id="cb1-82"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-82" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="bu">[</span> <span class="ot">!</span> <span class="ot">-f</span>  <span class="st">&quot;</span><span class="va">$USERNAME_NIX_FILE</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb1-83"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-83" aria-hidden="true" tabindex="-1"></a>  <span class="bu">echo</span> <span class="st">&quot;</span><span class="dt">\&quot;</span><span class="va">$USER</span><span class="dt">\&quot;</span><span class="st">&quot;</span> <span class="op">&gt;&gt;</span> <span class="st">&quot;</span><span class="va">$USERNAME_NIX_FILE</span><span class="st">&quot;</span></span>
<span id="cb1-84"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-84" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb1-85"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-85" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="ot">! </span><span class="bu">[</span> <span class="st">&quot;</span><span class="va">$OLD_HOME_MANAGER_ID</span><span class="st">&quot;</span> <span class="ot">=</span> <span class="st">&quot;</span><span class="va">$NEW_HOME_MANAGER_ID</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb1-86"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-86" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span></span>
<span id="cb1-87"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-87" aria-hidden="true" tabindex="-1"></a>    <span class="ex">highpr</span> <span class="st">&quot;home-manager switch&quot;</span></span>
<span id="cb1-88"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-88" aria-hidden="true" tabindex="-1"></a>    <span class="ex">home-manager</span> switch <span class="kw">||</span> <span class="dt">\</span></span>
<span id="cb1-89"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-89" aria-hidden="true" tabindex="-1"></a>      <span class="kw">(</span> <span class="ex">nix-channel</span> <span class="at">--update</span> <span class="kw">&amp;&amp;</span> <span class="ex">home-manager</span> switch <span class="kw">)</span> <span class="kw">||</span> <span class="dt">\</span></span>
<span id="cb1-90"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-90" aria-hidden="true" tabindex="-1"></a>      <span class="ex">fail</span> <span class="st">&quot;home-manager switch failed&quot;</span></span>
<span id="cb1-91"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-91" aria-hidden="true" tabindex="-1"></a>    <span class="ex">ok</span></span>
<span id="cb1-92"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-92" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span></span>
<span id="cb1-93"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-93" aria-hidden="true" tabindex="-1"></a>    <span class="ex">info</span> <span class="st">&quot;skipped&quot;</span></span>
<span id="cb1-94"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-94" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb1-95"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-95" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-96"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-96" aria-hidden="true" tabindex="-1"></a><span class="ex">highpr</span> <span class="st">&quot;doom-emacs&quot;</span></span>
<span id="cb1-97"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-97" aria-hidden="true" tabindex="-1"></a><span class="va">doompath</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$HOME</span><span class="st">/.emacs.d/bin/doom&quot;</span></span>
<span id="cb1-98"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-98" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="ot">! </span><span class="bu">[</span> <span class="st">&quot;</span><span class="va">$OLD_DOOM_PACKAGES</span><span class="st">&quot;</span> <span class="ot">=</span> <span class="st">&quot;</span><span class="va">$NEW_DOOM_PACKAGES</span><span class="st">&quot;</span> <span class="bu">]</span> <span class="kw">||</span> <span class="dt">\</span></span>
<span id="cb1-99"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-99" aria-hidden="true" tabindex="-1"></a>   <span class="ot">! </span><span class="bu">[</span> <span class="st">&quot;</span><span class="va">$OLD_DOOM_INIT</span><span class="st">&quot;</span>     <span class="ot">=</span> <span class="st">&quot;</span><span class="va">$NEW_DOOM_INIT</span><span class="st">&quot;</span>  <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb1-100"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-100" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="kw">[[</span> <span class="ot">-x</span> <span class="va">$doompath</span> <span class="kw">]];</span> <span class="cf">then</span></span>
<span id="cb1-101"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-101" aria-hidden="true" tabindex="-1"></a>      <span class="bu">echo</span></span>
<span id="cb1-102"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-102" aria-hidden="true" tabindex="-1"></a>      <span class="ex">highpr</span> <span class="st">&quot;doom sync&quot;</span></span>
<span id="cb1-103"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-103" aria-hidden="true" tabindex="-1"></a>      <span class="va">$doompath</span> sync <span class="kw">||</span> <span class="ex">fail</span> <span class="st">&quot;doom failed to sync&quot;</span></span>
<span id="cb1-104"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-104" aria-hidden="true" tabindex="-1"></a>      <span class="ex">ok</span></span>
<span id="cb1-105"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-105" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span></span>
<span id="cb1-106"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-106" aria-hidden="true" tabindex="-1"></a>     <span class="ex">fail</span> <span class="st">&quot;Cannot find doom executable at </span><span class="va">$doompath</span><span class="st">&quot;</span><span class="kw">;</span></span>
<span id="cb1-107"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-107" aria-hidden="true" tabindex="-1"></a>  <span class="cf">fi</span></span>
<span id="cb1-108"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-108" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span></span>
<span id="cb1-109"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-109" aria-hidden="true" tabindex="-1"></a>   <span class="ex">info</span> <span class="st">&quot;skipped&quot;</span></span>
<span id="cb1-110"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb1-110" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span></code></pre></div><h2 id="boostraping">Bootstrapping</h2><p>Bootstrapping this system is always a nice problem to think about. It
is smooth when everything is set but to bootstrap it I need binaries
installed by this system… So… How to handle the dependency cycle
correctly?</p><p>To minimize the pain, I removed more and more bootstrapping
dependencies. Now my almost single dependence for bootstrapping my
environment is <code class="verbatim">nix</code>. I haven't initialized
any machine for a long time now. The following should work.</p><ol><li><p>Use fish<a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> <code>chsh /bin/fish</code></p></li><li><p>Install nix
<code>curl -L https://nixos.org/nix/install | sh</code></p></li><li><p>Install home-manager</p><div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">nix-channel</span> <span class="at">--add</span> https://github.com/nix-community/home-manager/archive/release-21.05.tar.gz home-manager</span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ex">nix-channel</span> <span class="at">--update</span></span>
<span id="cb2-3"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">NIX_PATH</span><span class="op">=</span><span class="va">$HOME</span>/.nix-defexpr/channels<span class="va">${NIX_PATH</span><span class="op">:+</span>:<span class="va">}$NIX_PATH</span></span>
<span id="cb2-4"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="ex">nix-shell</span> <span class="st">&#39;&lt;home-manager&gt;&#39;</span> <span class="at">-A</span> install</span></code></pre></div></li><li><p>Install and use <code>yadm</code></p><div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">nix-shell</span> <span class="at">-p</span> yadm</span>
<span id="cb3-2"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="ex">yadm</span> boostrap</span>
<span id="cb3-3"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="ex">yadm</span> remote set-url origin <span class="op">&lt;</span>url-to-my-dot-files-repo<span class="op">&gt;</span></span>
<span id="cb3-4"><a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="ex">yadm</span> pull</span></code></pre></div></li><li><p>Still in the <code class="verbatim">nix-shell</code> with <code class="verbatim">yadm</code> run <code>~/bin/sync-env.sh</code></p></li></ol><p>There is a risk that step 3 fail because I pin most of my packages in
home-manager configuration, and it will try to install <code class="verbatim">yadm</code>. This can conflict with the <code class="verbatim">yadm</code> installed in the current <code class="verbatim">nix-shell</code>. So sometime I need to:</p><ol><li>Remove the line installing <code class="verbatim">yadm</code> in my
home-manager configuration first</li><li>run <code class="verbatim">home-manager sync</code></li><li>get out of the <code class="verbatim">nix-shell</code>,</li><li>add <code class="verbatim">yadm</code> back in the <code class="verbatim">home-manager</code> config</li><li>run <code class="verbatim">home-manager sync</code> again, but this
time out of the <code class="verbatim">nix-shell</code>.</li><li>Finally I can run my <code class="verbatim">~/bin/sync-env.sh</code>
command.</li></ol><p>So this post will probably be useful as a personal note in the
future. Because bootstrapping is generally not trivial. I will probably
update this post if something is missing.</p><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr></hr><ol><li id="fn1"><p>I use fish for interactive shell. I use <code>zsh</code>
for quick dirty scripts (a lot better than bash), and I switch to <a href="https://hackage.haskell.org/package/turtle">turtle</a> if I need
to be serious about the script.<a href="https://yannesposito.com/posts/0021-my-personal-environment-sync/index.html#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section></article></main>
]]></description>
</item>


<item>
<title>iA writer clone within doom-emacs</title>
<guid>https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html</guid>
<pubDate>Sun, 24 Oct 2021 00:00:00 +0200</pubDate>
<category>emacs</category>
<category>org-mode</category>
<description><![CDATA[
<main id="content"><article><p>So I played with tweaking my emacs configuration again. I think I
made something worth to be shared. I wanted to have the same effect than
in iA writer within emacs. And I just reached this.</p><p>So the effect I am looking to achieve can be seen in this video.</p><video src="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/zen-writer-demo.mov" width="100%" controls="(null)" autoplay="(null)">
</video><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/zen-writer-demo.mov">zen-writer-demo video (7.2MB)</a><p>It highlight the current sentence (in black) while the rest of the
text is gray. The main issue with the <code class="verbatim">hl-sentence</code> package alone is that it set a
specific face to the current sentence, but does not affect the other
text in the buffer. In fact to make it work as I would expect, you need
to make the default color grey, and only set black for the highlighted
text.</p><p>Fortunately, I have recently created a personal theme close to that.
I just created a new specific one. Everything is mostly "gray" except
the font <code>hl-sentence</code> which is black.</p><p>And that's it. So to make it all work I need.</p><ul><li>a Gray theme,</li><li>the package <code>hl-sentence</code>,</li><li>the <code>writeroom</code> package.</li></ul><p>So how to achieve that yourself? Here is how I do it.</p><h2 id="the-zen-writer-theme">The zen-writer theme</h2><p>Download <a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/doom-zen-writer-theme.el">Doom Zen-Writer
theme</a>. This depend on doom-themes and here is the code of the theme.
Just put it in you <code class="verbatim">~/.doom.d/themes</code>
directory.</p><h2 id="add-this-is-your--packages-el-">add this is your
<code>packages.el</code></h2><pre class="elisp"><code>(package! hl-sentence
  :pin &quot;86ae38d3103bd20da5485cbdd59dfbd396c45ee4&quot;)
</code></pre><h2 id="helpers">Helpers</h2><p>You probably want to be able to put you out of this writing mode.
Here is a <a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/zen-writer.el">zen-writer.el</a> file that contain my
keymaps and useful functions.</p><p>Put it in you <code class="verbatim">~/.doom.d/</code> directory and
in you <code class="verbatim">config.el</code> put</p><div class="sourceCode" id="cb2" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb2-1"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a>(load! <span class="st">&quot;~/.doom.d/zen-writer.el&quot;</span>)</span></code></pre></div><p>And with this you should pass to zen mode with
<code>SPC y z z</code>.</p><p>To make the un-zen works. You will need to have a
<code>y/auto-update-theme</code> function that set your current theme.
My function change the theme depending on the time of the day or the day
of the week.</p><p>So here it is for inspiration:</p><div class="sourceCode" id="cb3" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb3-1"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">defun</span><span class="fu"> y/auto-update-theme </span>()</span>
<span id="cb3-2"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;depending on time use different theme&quot;</span></span>
<span id="cb3-3"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; very early =&gt; gruvbox-light, solarized-light, nord-light</span></span>
<span id="cb3-4"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-4" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">let*</span> ((day-of-week (format-time-string <span class="st">&quot;%a&quot;</span>))</span>
<span id="cb3-5"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-5" aria-hidden="true" tabindex="-1"></a>         (week-end? (<span class="kw">or</span> (<span class="kw">equal</span> <span class="st">&quot;Sat&quot;</span> day-of-week)</span>
<span id="cb3-6"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-6" aria-hidden="true" tabindex="-1"></a>                        (<span class="kw">equal</span> <span class="st">&quot;Sun&quot;</span> day-of-week)))</span>
<span id="cb3-7"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-7" aria-hidden="true" tabindex="-1"></a>         (hour (<span class="kw">nth</span> <span class="dv">2</span> (decode-time (current-time))))</span>
<span id="cb3-8"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-8" aria-hidden="true" tabindex="-1"></a>         (time-to-sleep? (<span class="kw">or</span> (<span class="op">&gt;</span> hour <span class="dv">22</span>) (<span class="op">&lt;</span> hour <span class="dv">7</span>)))</span>
<span id="cb3-9"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-10"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-10" aria-hidden="true" tabindex="-1"></a>         (theme (<span class="kw">cond</span></span>
<span id="cb3-11"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-11" aria-hidden="true" tabindex="-1"></a>                 (time-to-sleep?  <span class="dt">&#39;doom-plain-dark</span>)</span>
<span id="cb3-12"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-12" aria-hidden="true" tabindex="-1"></a>                 (week-end?       <span class="dt">&#39;doom-nord-light</span>)</span>
<span id="cb3-13"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-13" aria-hidden="true" tabindex="-1"></a>                 ((<span class="op">&lt;=</span> <span class="dv">7</span> hour <span class="dv">8</span>)   <span class="dt">&#39;doom-gruvbox-light</span>)</span>
<span id="cb3-14"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-14" aria-hidden="true" tabindex="-1"></a>                 ((<span class="op">=</span> <span class="dv">9</span> hour)      <span class="dt">&#39;doom-solarized-light</span>)</span>
<span id="cb3-15"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-15" aria-hidden="true" tabindex="-1"></a>                 ((<span class="op">&lt;=</span> <span class="dv">10</span> hour <span class="dv">16</span>) <span class="dt">&#39;doom-solarized-white</span>)</span>
<span id="cb3-16"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-16" aria-hidden="true" tabindex="-1"></a>                 ((<span class="op">&lt;=</span> <span class="dv">17</span> hour <span class="dv">18</span>) <span class="dt">&#39;doom-gruvbox-light</span>)</span>
<span id="cb3-17"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-17" aria-hidden="true" tabindex="-1"></a>                 ((<span class="op">&lt;=</span> <span class="dv">19</span> hour <span class="dv">22</span>) <span class="dt">&#39;doom-oceanic-next</span>))))</span>
<span id="cb3-18"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-18" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">when</span> (<span class="kw">not</span> (<span class="kw">equal</span> doom-theme theme))</span>
<span id="cb3-19"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-19" aria-hidden="true" tabindex="-1"></a>      (<span class="kw">setq</span> doom-theme theme)</span>
<span id="cb3-20"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-20" aria-hidden="true" tabindex="-1"></a>      (load-theme doom-theme <span class="kw">t</span>))</span>
<span id="cb3-21"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-21" aria-hidden="true" tabindex="-1"></a>    <span class="co">;; run that function again next hour</span></span>
<span id="cb3-22"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb3-22" aria-hidden="true" tabindex="-1"></a>    (run-at-time (<span class="kw">format</span> <span class="st">&quot;%02d:%02d&quot;</span> (<span class="op">+</span> hour <span class="dv">1</span>) <span class="dv">0</span>) <span class="kw">nil</span> <span class="dt">&#39;y/auto-update-theme</span>)))</span></code></pre></div><h2 id="bonus">Bonus</h2><p>I use Nerd Fonts and in particular the font `iMWritingDuoS` which is
I think a clone of the iAWriter font.</p><div class="sourceCode" id="cb4" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb4-1"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb4-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">setq</span> doom-variable-pitch-font</span>
<span id="cb4-2"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb4-2" aria-hidden="true" tabindex="-1"></a>      (font-spec :family <span class="st">&quot;iMWritingDuoS Nerd Font&quot;</span> :size <span class="dv">12</span>))</span></code></pre></div><p>I hope you find this useful. I really like how it looks now.</p><h2 id="annex">Annex</h2><p>The code source used in this article.</p><h3 id="zen-writer">zen-writer</h3><div class="sourceCode" id="zen-writer.el" data-org-language="emacs-lisp" data-tangle="zen-writer.el"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="zen-writer.el-1"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-1" aria-hidden="true" tabindex="-1"></a><span class="co">;;; zen-writer.el -*- lexical-binding: t; -*-</span></span>
<span id="zen-writer.el-2"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="zen-writer.el-3"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-3" aria-hidden="true" tabindex="-1"></a>(<span class="kw">defun</span><span class="fu"> y/zen </span>()</span>
<span id="zen-writer.el-4"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-4" aria-hidden="true" tabindex="-1"></a>  (interactive)</span>
<span id="zen-writer.el-5"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-5" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> doom-theme <span class="dt">&#39;doom-zen-writer</span>)</span>
<span id="zen-writer.el-6"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-6" aria-hidden="true" tabindex="-1"></a>  (load-theme doom-theme <span class="kw">t</span>)</span>
<span id="zen-writer.el-7"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-7" aria-hidden="true" tabindex="-1"></a>  (hl-sentence-mode +1))</span>
<span id="zen-writer.el-8"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="zen-writer.el-9"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-9" aria-hidden="true" tabindex="-1"></a>(<span class="kw">defun</span><span class="fu"> y/unzen </span>()</span>
<span id="zen-writer.el-10"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-10" aria-hidden="true" tabindex="-1"></a>  (interactive)</span>
<span id="zen-writer.el-11"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-11" aria-hidden="true" tabindex="-1"></a>  (y/auto-update-theme)</span>
<span id="zen-writer.el-12"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-12" aria-hidden="true" tabindex="-1"></a>  (hl-sentence-mode -1))</span>
<span id="zen-writer.el-13"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="zen-writer.el-14"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-14" aria-hidden="true" tabindex="-1"></a>(<span class="kw">defun</span><span class="fu"> y/zen-full </span>()</span>
<span id="zen-writer.el-15"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-15" aria-hidden="true" tabindex="-1"></a>  (interactive)</span>
<span id="zen-writer.el-16"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-16" aria-hidden="true" tabindex="-1"></a>  (y/zen)</span>
<span id="zen-writer.el-17"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-17" aria-hidden="true" tabindex="-1"></a>  (toggle-frame-fullscreen)</span>
<span id="zen-writer.el-18"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-18" aria-hidden="true" tabindex="-1"></a>  (doom-big-font-mode +1))</span>
<span id="zen-writer.el-19"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="zen-writer.el-20"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-20" aria-hidden="true" tabindex="-1"></a>(<span class="kw">defun</span><span class="fu"> y/unzen-full </span>()</span>
<span id="zen-writer.el-21"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-21" aria-hidden="true" tabindex="-1"></a>  (interactive)</span>
<span id="zen-writer.el-22"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-22" aria-hidden="true" tabindex="-1"></a>  (y/unzen)</span>
<span id="zen-writer.el-23"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-23" aria-hidden="true" tabindex="-1"></a>  (toggle-frame-fullscreen)</span>
<span id="zen-writer.el-24"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-24" aria-hidden="true" tabindex="-1"></a>  (doom-big-font-mode -1))</span>
<span id="zen-writer.el-25"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="zen-writer.el-26"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-26" aria-hidden="true" tabindex="-1"></a>(map! :leader</span>
<span id="zen-writer.el-27"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-27" aria-hidden="true" tabindex="-1"></a>      (:prefix (<span class="st">&quot;y z&quot;</span> . <span class="st">&quot;Zen Writer&quot;</span>)</span>
<span id="zen-writer.el-28"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-28" aria-hidden="true" tabindex="-1"></a>       :desc <span class="st">&quot;Full Zen Writer&quot;</span> <span class="st">&quot;z&quot;</span> <span class="op">#&#39;</span>y/zen-full</span>
<span id="zen-writer.el-29"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-29" aria-hidden="true" tabindex="-1"></a>       :desc <span class="st">&quot;un-Full Zen Writer&quot;</span> <span class="st">&quot;u&quot;</span> <span class="op">#&#39;</span>y/unzen-full</span>
<span id="zen-writer.el-30"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-30" aria-hidden="true" tabindex="-1"></a>       :desc <span class="st">&quot;Zen Writer&quot;</span> <span class="st">&quot;t&quot;</span> <span class="op">#&#39;</span>y/zen</span>
<span id="zen-writer.el-31"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#zen-writer.el-31" aria-hidden="true" tabindex="-1"></a>       :desc <span class="st">&quot;un-Zen Writer&quot;</span> <span class="st">&quot;q&quot;</span> <span class="op">#&#39;</span>y/unzen))</span></code></pre></div><h3 id="zen-writer-doom-theme">zen-writer doom theme</h3><div class="sourceCode" id="cb5" data-org-language="emacs-lisp" data-tangle="doom-zen-writer-theme.el"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb5-1"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">;;; doom-zen-writer-theme.el --- -*- lexical-binding: t; no-byte-compile: t; -*-</span></span>
<span id="cb5-2"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb5-3"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co">;; Author: Yann Esposito &lt;https://github.com/yogsototh&gt;</span></span>
<span id="cb5-4"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co">;; Created: October 24, 2021</span></span>
<span id="cb5-5"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="co">;; Version: 1.0.0</span></span>
<span id="cb5-6"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="co">;; Keywords: custom themes, faces</span></span>
<span id="cb5-7"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="co">;; Homepage: https://github.com/hlissner/emacs-doom-themes</span></span>
<span id="cb5-8"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="co">;; Package-Requires: ((emacs &quot;25.1&quot;) (cl-lib &quot;0.5&quot;) (doom-themes &quot;2.2.1&quot;))</span></span>
<span id="cb5-9"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb5-10"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co">;;; Code:</span></span>
<span id="cb5-11"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-12"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-12" aria-hidden="true" tabindex="-1"></a>(<span class="kw">require</span> <span class="dt">&#39;doom-themes</span>)</span>
<span id="cb5-13"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-14"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb5-15"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="co">;;; Variables</span></span>
<span id="cb5-16"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-17"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-17" aria-hidden="true" tabindex="-1"></a>(defgroup doom-plain-theme <span class="kw">nil</span></span>
<span id="cb5-18"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-18" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;Options for the `doom-plain&#39; theme.&quot;</span></span>
<span id="cb5-19"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-19" aria-hidden="true" tabindex="-1"></a>  :group <span class="dt">&#39;doom-themes</span>)</span>
<span id="cb5-20"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-21"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-21" aria-hidden="true" tabindex="-1"></a>(defcustom doom-plain-padded-modeline doom-themes-padded-modeline</span>
<span id="cb5-22"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-22" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;If non-nil, adds a 4px padding to the mode-line.</span></span>
<span id="cb5-23"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-23" aria-hidden="true" tabindex="-1"></a><span class="st">Can be an integer to determine the exact padding.&quot;</span></span>
<span id="cb5-24"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-24" aria-hidden="true" tabindex="-1"></a>  :group <span class="dt">&#39;doom-plain-theme</span></span>
<span id="cb5-25"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-25" aria-hidden="true" tabindex="-1"></a>  <span class="bu">:type</span> &#39;(<span class="kw">or</span> <span class="kw">integer</span> <span class="kw">boolean</span>))</span>
<span id="cb5-26"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-27"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-28"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-28" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb5-29"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-29" aria-hidden="true" tabindex="-1"></a><span class="co">;;; Theme definition</span></span>
<span id="cb5-30"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-31"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-31" aria-hidden="true" tabindex="-1"></a>(def-doom-theme doom-zen-writer</span>
<span id="cb5-32"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-32" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;Theme inspired by gko&#39;s plain.&quot;</span></span>
<span id="cb5-33"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-34"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-34" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; name      default/256/16</span></span>
<span id="cb5-35"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-35" aria-hidden="true" tabindex="-1"></a>  ((bg         &#39;(<span class="st">&quot;#ffffff&quot;</span>))</span>
<span id="cb5-36"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-36" aria-hidden="true" tabindex="-1"></a>   (bg-alt     &#39;(<span class="st">&quot;#eaecea&quot;</span>))</span>
<span id="cb5-37"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-37" aria-hidden="true" tabindex="-1"></a>   (base0      &#39;(<span class="st">&quot;#969896&quot;</span>))</span>
<span id="cb5-38"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-38" aria-hidden="true" tabindex="-1"></a>   (base1      &#39;(<span class="st">&quot;#f1f3f5&quot;</span>))</span>
<span id="cb5-39"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-39" aria-hidden="true" tabindex="-1"></a>   (base2      &#39;(<span class="st">&quot;#606666&quot;</span>))</span>
<span id="cb5-40"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-40" aria-hidden="true" tabindex="-1"></a>   (base3      &#39;(<span class="st">&quot;#cccccc&quot;</span>))</span>
<span id="cb5-41"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-41" aria-hidden="true" tabindex="-1"></a>   (base4      &#39;(<span class="st">&quot;#e7e7e7&quot;</span>))</span>
<span id="cb5-42"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-42" aria-hidden="true" tabindex="-1"></a>   (base5      &#39;(<span class="st">&quot;#a5a8a6&quot;</span>))</span>
<span id="cb5-43"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-43" aria-hidden="true" tabindex="-1"></a>   (base6      &#39;(<span class="st">&quot;#fafafa&quot;</span>))</span>
<span id="cb5-44"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-44" aria-hidden="true" tabindex="-1"></a>   (base7      &#39;(<span class="st">&quot;#dfdfdf&quot;</span>))</span>
<span id="cb5-45"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-45" aria-hidden="true" tabindex="-1"></a>   (base8      &#39;(<span class="st">&quot;#fafafa&quot;</span>))</span>
<span id="cb5-46"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-46" aria-hidden="true" tabindex="-1"></a>   (fg         &#39;(<span class="st">&quot;#969896&quot;</span>))</span>
<span id="cb5-47"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-47" aria-hidden="true" tabindex="-1"></a>   (fg-alt     (doom-lighten fg <span class="fl">0.15</span>))</span>
<span id="cb5-48"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-49"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-49" aria-hidden="true" tabindex="-1"></a>   (grey       fg)</span>
<span id="cb5-50"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-50" aria-hidden="true" tabindex="-1"></a>   (red        fg)</span>
<span id="cb5-51"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-51" aria-hidden="true" tabindex="-1"></a>   (blue       fg)</span>
<span id="cb5-52"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-52" aria-hidden="true" tabindex="-1"></a>   (dark-blue  fg)</span>
<span id="cb5-53"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-53" aria-hidden="true" tabindex="-1"></a>   (orange     fg)</span>
<span id="cb5-54"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-54" aria-hidden="true" tabindex="-1"></a>   (green      fg)</span>
<span id="cb5-55"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-55" aria-hidden="true" tabindex="-1"></a>   (teal       fg)</span>
<span id="cb5-56"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-56" aria-hidden="true" tabindex="-1"></a>   (yellow     fg)</span>
<span id="cb5-57"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-57" aria-hidden="true" tabindex="-1"></a>   (magenta    fg)</span>
<span id="cb5-58"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-58" aria-hidden="true" tabindex="-1"></a>   (violet     fg)</span>
<span id="cb5-59"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-59" aria-hidden="true" tabindex="-1"></a>   (cyan       fg)</span>
<span id="cb5-60"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-60" aria-hidden="true" tabindex="-1"></a>   (dark-cyan  fg)</span>
<span id="cb5-61"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-62"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-62" aria-hidden="true" tabindex="-1"></a>   <span class="co">;; face categories -- required for all themes</span></span>
<span id="cb5-63"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-63" aria-hidden="true" tabindex="-1"></a>   (highlight      base2)</span>
<span id="cb5-64"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-64" aria-hidden="true" tabindex="-1"></a>   (vertical-bar   base5)</span>
<span id="cb5-65"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-65" aria-hidden="true" tabindex="-1"></a>   (selection      base1)</span>
<span id="cb5-66"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-66" aria-hidden="true" tabindex="-1"></a>   (builtin        base0)</span>
<span id="cb5-67"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-67" aria-hidden="true" tabindex="-1"></a>   (comments       base5)</span>
<span id="cb5-68"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-68" aria-hidden="true" tabindex="-1"></a>   (doc-comments   base5)</span>
<span id="cb5-69"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-69" aria-hidden="true" tabindex="-1"></a>   (constants      base0)</span>
<span id="cb5-70"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-70" aria-hidden="true" tabindex="-1"></a>   (functions      fg)</span>
<span id="cb5-71"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-71" aria-hidden="true" tabindex="-1"></a>   (keywords       fg)</span>
<span id="cb5-72"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-72" aria-hidden="true" tabindex="-1"></a>   (methods        fg)</span>
<span id="cb5-73"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-73" aria-hidden="true" tabindex="-1"></a>   (operators      fg)</span>
<span id="cb5-74"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-74" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">type</span>           fg)</span>
<span id="cb5-75"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-75" aria-hidden="true" tabindex="-1"></a>   (strings        base0)</span>
<span id="cb5-76"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-76" aria-hidden="true" tabindex="-1"></a>   (variables      base0)</span>
<span id="cb5-77"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-77" aria-hidden="true" tabindex="-1"></a>   (numbers        base0)</span>
<span id="cb5-78"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-78" aria-hidden="true" tabindex="-1"></a>   (region         base4)</span>
<span id="cb5-79"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-79" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">error</span>          (doom-blend fg <span class="st">&quot;#ff0000&quot;</span> <span class="fl">0.4</span>))</span>
<span id="cb5-80"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-80" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">warning</span>        base2)</span>
<span id="cb5-81"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-81" aria-hidden="true" tabindex="-1"></a>   (success        green)</span>
<span id="cb5-82"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-82" aria-hidden="true" tabindex="-1"></a>   (vc-modified    base5)</span>
<span id="cb5-83"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-83" aria-hidden="true" tabindex="-1"></a>   (vc-added       (doom-lighten fg <span class="fl">0.7</span>))</span>
<span id="cb5-84"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-84" aria-hidden="true" tabindex="-1"></a>   (vc-deleted     base2)</span>
<span id="cb5-85"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-85" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-86"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-86" aria-hidden="true" tabindex="-1"></a>   <span class="co">;; custom categories</span></span>
<span id="cb5-87"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-87" aria-hidden="true" tabindex="-1"></a>   (-modeline-pad</span>
<span id="cb5-88"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-88" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">when</span> doom-plain-padded-modeline</span>
<span id="cb5-89"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-89" aria-hidden="true" tabindex="-1"></a>      (<span class="kw">if</span> (<span class="kw">integerp</span> doom-plain-padded-modeline) doom-plain-padded-modeline <span class="dv">4</span>)))</span>
<span id="cb5-90"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-90" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-91"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-91" aria-hidden="true" tabindex="-1"></a>   (modeline-bg              (doom-darken bg-alt <span class="fl">0.15</span>))</span>
<span id="cb5-92"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-92" aria-hidden="true" tabindex="-1"></a>   (modeline-bg-alt          (doom-darken bg-alt <span class="fl">0.1</span>))</span>
<span id="cb5-93"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-93" aria-hidden="true" tabindex="-1"></a>   (modeline-bg-inactive     (doom-darken bg-alt <span class="fl">0.1</span>))</span>
<span id="cb5-94"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-94" aria-hidden="true" tabindex="-1"></a>   (modeline-bg-inactive-alt bg-alt)</span>
<span id="cb5-95"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-95" aria-hidden="true" tabindex="-1"></a>   (modeline-fg              fg)</span>
<span id="cb5-96"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-96" aria-hidden="true" tabindex="-1"></a>   (modeline-fg-alt          (doom-darken modeline-bg-inactive <span class="fl">0.35</span>)))</span>
<span id="cb5-97"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-97" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-98"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-98" aria-hidden="true" tabindex="-1"></a>  <span class="co">;;;; Base theme face overrides</span></span>
<span id="cb5-99"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-99" aria-hidden="true" tabindex="-1"></a>  ((<span class="kw">error</span>   :underline `(:style wave :color ,<span class="kw">error</span>))</span>
<span id="cb5-100"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-100" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">warning</span> :underline `(:style wave :color ,<span class="kw">warning</span>))</span>
<span id="cb5-101"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-101" aria-hidden="true" tabindex="-1"></a>   (hl-sentence :foreground <span class="st">&quot;#000000&quot;</span> :background bg)</span>
<span id="cb5-102"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-102" aria-hidden="true" tabindex="-1"></a>   ((font-lock-constant-face &amp;override)      :slant <span class="dt">&#39;italic</span>)</span>
<span id="cb5-103"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-103" aria-hidden="true" tabindex="-1"></a>   ((font-lock-comment-face &amp;override)       :slant <span class="dt">&#39;italic</span>)</span>
<span id="cb5-104"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-104" aria-hidden="true" tabindex="-1"></a>   ((font-lock-function-name-face &amp;override) :slant <span class="dt">&#39;italic</span>)</span>
<span id="cb5-105"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-105" aria-hidden="true" tabindex="-1"></a>   ((font-lock-type-face &amp;override)          :slant <span class="dt">&#39;italic</span>)</span>
<span id="cb5-106"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-106" aria-hidden="true" tabindex="-1"></a>   (hl-line :background base8)</span>
<span id="cb5-107"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-107" aria-hidden="true" tabindex="-1"></a>   ((line-number &amp;override) :foreground base3)</span>
<span id="cb5-108"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-108" aria-hidden="true" tabindex="-1"></a>   ((line-number-current-line &amp;override) :foreground base2)</span>
<span id="cb5-109"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-109" aria-hidden="true" tabindex="-1"></a>   (mode-line</span>
<span id="cb5-110"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-110" aria-hidden="true" tabindex="-1"></a>    :background modeline-bg :foreground modeline-fg</span>
<span id="cb5-111"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-111" aria-hidden="true" tabindex="-1"></a>    :box (<span class="kw">if</span> -modeline-pad `(:line-width ,-modeline-pad :color ,modeline-bg)))</span>
<span id="cb5-112"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-112" aria-hidden="true" tabindex="-1"></a>   (mode-line-inactive</span>
<span id="cb5-113"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-113" aria-hidden="true" tabindex="-1"></a>    :background modeline-bg-inactive :foreground modeline-fg-alt</span>
<span id="cb5-114"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-114" aria-hidden="true" tabindex="-1"></a>    :box (<span class="kw">if</span> -modeline-pad `(:line-width ,-modeline-pad :color ,modeline-bg-inactive)))</span>
<span id="cb5-115"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-115" aria-hidden="true" tabindex="-1"></a>   (mode-line-emphasis :foreground highlight)</span>
<span id="cb5-116"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-116" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-117"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-117" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; doom-modeline</span></span>
<span id="cb5-118"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-118" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-bar :background modeline-bg)</span>
<span id="cb5-119"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-119" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-bar-inactive :inherit <span class="dt">&#39;doom-modeline-bar</span>)</span>
<span id="cb5-120"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-120" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-project-dir :foreground fg)</span>
<span id="cb5-121"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-121" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-buffer-file :foreground fg)</span>
<span id="cb5-122"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-122" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-buffer-modified :weight <span class="dt">&#39;bold</span> :foreground <span class="st">&quot;#000000&quot;</span>)</span>
<span id="cb5-123"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-123" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-panel :inherit <span class="dt">&#39;mode-line-highlight</span> :background base3 :foreground fg)</span>
<span id="cb5-124"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-124" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; ivy</span></span>
<span id="cb5-125"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-125" aria-hidden="true" tabindex="-1"></a>   (ivy-posframe :background bg-alt)</span>
<span id="cb5-126"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-126" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; magit</span></span>
<span id="cb5-127"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-127" aria-hidden="true" tabindex="-1"></a>   ((magit-diff-added-highlight &amp;override)   :foreground fg :background (doom-blend vc-added bg <span class="fl">0.3</span>))</span>
<span id="cb5-128"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-128" aria-hidden="true" tabindex="-1"></a>   ((magit-diff-removed &amp;override)           :foreground (doom-lighten fg <span class="fl">0.4</span>) :background (doom-blend vc-deleted bg <span class="fl">0.1</span>))</span>
<span id="cb5-129"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-129" aria-hidden="true" tabindex="-1"></a>   ((magit-diff-removed-highlight &amp;override) :foreground fg :background (doom-blend vc-deleted bg <span class="fl">0.22</span>))</span>
<span id="cb5-130"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-130" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; lsp-mode</span></span>
<span id="cb5-131"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-131" aria-hidden="true" tabindex="-1"></a>   (lsp-headerline-breadcrumb-symbols-face :foreground keywords :weight <span class="dt">&#39;bold</span>)</span>
<span id="cb5-132"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-132" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; outline &lt;built-in&gt;</span></span>
<span id="cb5-133"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-133" aria-hidden="true" tabindex="-1"></a>   (outline-1 :slant <span class="dt">&#39;italic</span> :foreground fg-alt)</span>
<span id="cb5-134"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-134" aria-hidden="true" tabindex="-1"></a>   (outline-2 :inherit <span class="dt">&#39;outline-1</span> :foreground base2)</span>
<span id="cb5-135"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-135" aria-hidden="true" tabindex="-1"></a>   (outline-3 :inherit <span class="dt">&#39;outline-2</span>)</span>
<span id="cb5-136"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-136" aria-hidden="true" tabindex="-1"></a>   (outline-4 :inherit <span class="dt">&#39;outline-3</span>)</span>
<span id="cb5-137"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-137" aria-hidden="true" tabindex="-1"></a>   (outline-5 :inherit <span class="dt">&#39;outline-4</span>)</span>
<span id="cb5-138"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-138" aria-hidden="true" tabindex="-1"></a>   (outline-6 :inherit <span class="dt">&#39;outline-5</span>)</span>
<span id="cb5-139"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-139" aria-hidden="true" tabindex="-1"></a>   (outline-7 :inherit <span class="dt">&#39;outline-6</span>)</span>
<span id="cb5-140"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-140" aria-hidden="true" tabindex="-1"></a>   (outline-8 :inherit <span class="dt">&#39;outline-7</span>)</span>
<span id="cb5-141"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-141" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; org &lt;built-in&gt;</span></span>
<span id="cb5-142"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-142" aria-hidden="true" tabindex="-1"></a>   ((org-block &amp;override) :background bg-alt)</span>
<span id="cb5-143"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-143" aria-hidden="true" tabindex="-1"></a>   ((org-block-begin-line &amp;override) :foreground base5)</span>
<span id="cb5-144"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-144" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; solaire-mode</span></span>
<span id="cb5-145"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-145" aria-hidden="true" tabindex="-1"></a>   (solaire-mode-line-face</span>
<span id="cb5-146"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-146" aria-hidden="true" tabindex="-1"></a>    :inherit <span class="dt">&#39;mode-line</span></span>
<span id="cb5-147"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-147" aria-hidden="true" tabindex="-1"></a>    :background modeline-bg-alt</span>
<span id="cb5-148"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-148" aria-hidden="true" tabindex="-1"></a>    :box (<span class="kw">if</span> -modeline-pad `(:line-width ,-modeline-pad :color ,modeline-bg-alt)))</span>
<span id="cb5-149"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-149" aria-hidden="true" tabindex="-1"></a>   (solaire-mode-line-inactive-face</span>
<span id="cb5-150"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-150" aria-hidden="true" tabindex="-1"></a>    :inherit <span class="dt">&#39;mode-line-inactive</span></span>
<span id="cb5-151"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-151" aria-hidden="true" tabindex="-1"></a>    :background modeline-bg-inactive-alt</span>
<span id="cb5-152"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-152" aria-hidden="true" tabindex="-1"></a>    :box (<span class="kw">if</span> -modeline-pad `(:line-width ,-modeline-pad :color ,modeline-bg-inactive-alt)))))</span>
<span id="cb5-153"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-153" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-154"><a href="https://yannesposito.com/posts/0021-ia-writer-clone-within-doom-emacs/index.html#cb5-154" aria-hidden="true" tabindex="-1"></a><span class="co">;;; doom-zen-writer-theme.el ends here</span></span></code></pre></div></article></main>
]]></description>
</item>


<item>
<title>Cool looking org-mode</title>
<guid>https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html</guid>
<pubDate>Sat, 21 Aug 2021 00:00:00 +0200</pubDate>
<category>org-mode</category>
<category>emacs</category>
<description><![CDATA[
<main id="content"><article><div class="notes"><p>TL;DR: My tweaked configuration to make org-mode even more pleasant
to use.</p></div><h2 id="the-code">The code</h2><p>At the end of this article there is a long digression about why I
ended up here. But instead of bothering you with the why here is a what
it looks like, and how to achieve it.</p><p>First you need to install some dependencies.</p><ol><li>Install nerdfonts<a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></li><li>Tell org-mode to use <code class="verbatim">variable-pitch-mode</code> (variable width font)</li><li>Use <code>(setq org-hide-emphasis-markers t)</code></li><li>Configure a lot of org-mode specific faces to still use a monospaced
font.</li></ol><p>Here are some images of the result. Notice one important factor of
the feel is that I work on a mac with retina display. Often font
rendering feel too bold by default. But this is perfect to have a
writing environment even if screenshot does not look as slick as other
ones, the usage is superior.</p><figure><img src="https://yannesposito.com/posts/0020-cool-looking-org-mode/top.png.webp" title="top" alt="org-mode headers"></img><figcaption aria-hidden="true">org-mode headers</figcaption></figure><figure><img src="https://yannesposito.com/posts/0020-cool-looking-org-mode/img-with-caption.png.webp" title="img-with-caption" alt="org-mode some inline image"></img><figcaption aria-hidden="true">org-mode some inline image</figcaption></figure><figure><img src="https://yannesposito.com/posts/0020-cool-looking-org-mode/code.png.webp" title="img-with-caption" alt="org-mode with some code block"></img><figcaption aria-hidden="true">org-mode with some code
block</figcaption></figure><figure><img src="https://yannesposito.com/posts/0020-cool-looking-org-mode/y-org-mode.png.webp" title="nano-emacs" alt="Org mode with a modified doom-solarized light theme (use a grey background)"></img><figcaption aria-hidden="true">Org mode with a modified doom-solarized
light theme (use a grey background)</figcaption></figure><p>The main trick is to change org-mode to use different font depending
on the kind of bloc. I use two fonts variant which are an iA Writer
clone fonts; iM Writing Nerd Font.</p><p>First you need to install nerd-fonts<a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>.
You will get that <code class="verbatim">iMWritingDuoS Nerd Font</code>.
If you look at the code block; I support the case when the font is not
installed and fall back to Georgia or PT Serif. One nice little bonus of
the config is to make the fixed width fonts smaller. This is often
something I like when writing in org-mode.</p><p>There is a minor dependency on <code class="verbatim">doom</code> as
I use <code class="verbatim">doom-color</code> for the color of the
links. But you could easily use any color you like if you do not use
doom.</p><div class="sourceCode" id="cb1" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb1-1"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">setq</span> org-ellipsis <span class="st">&quot; [+]&quot;</span>)</span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a>(add-hook <span class="dt">&#39;org-mode-hook</span> <span class="dt">&#39;variable-pitch-mode</span>)</span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a>(<span class="kw">let*</span> ((variable-tuple</span>
<span id="cb1-4"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-4" aria-hidden="true" tabindex="-1"></a>        (<span class="kw">cond</span></span>
<span id="cb1-5"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-5" aria-hidden="true" tabindex="-1"></a>         ((x-list-fonts <span class="st">&quot;iMWritingDuoS Nerd Font&quot;</span>)   &#39;(:family <span class="st">&quot;iMWritingDuoS Nerd Font&quot;</span>))</span>
<span id="cb1-6"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-6" aria-hidden="true" tabindex="-1"></a>         ((x-list-fonts <span class="st">&quot;Georgia&quot;</span>)   &#39;(:family <span class="st">&quot;Georgia&quot;</span>))</span>
<span id="cb1-7"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-7" aria-hidden="true" tabindex="-1"></a>         ((x-list-fonts <span class="st">&quot;PT Serif&quot;</span>)  &#39;(:family <span class="st">&quot;PT Serif&quot;</span>))))</span>
<span id="cb1-8"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-8" aria-hidden="true" tabindex="-1"></a>       (fixed-tuple</span>
<span id="cb1-9"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-9" aria-hidden="true" tabindex="-1"></a>        (<span class="kw">cond</span></span>
<span id="cb1-10"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-10" aria-hidden="true" tabindex="-1"></a>         ((x-list-fonts <span class="st">&quot;iMWritingDuoS Nerd Font Mono&quot;</span>)   &#39;(:family <span class="st">&quot;iMWritingDuoS Nerd Font Mono&quot;</span>   :height <span class="dv">160</span>))</span>
<span id="cb1-11"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-11" aria-hidden="true" tabindex="-1"></a>         ((x-list-fonts <span class="st">&quot;Menlo&quot;</span>)               &#39;(:family <span class="st">&quot;Menlo&quot;</span>    :height <span class="dv">120</span>))</span>
<span id="cb1-12"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-12" aria-hidden="true" tabindex="-1"></a>         ((x-list-fonts <span class="st">&quot;PT Mono&quot;</span>)             &#39;(:family <span class="st">&quot;PT Mono&quot;</span>  :height <span class="dv">120</span>))))</span>
<span id="cb1-13"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-13" aria-hidden="true" tabindex="-1"></a>       (headline `(:inherit default :weight bold)))</span>
<span id="cb1-14"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-14" aria-hidden="true" tabindex="-1"></a>  (custom-theme-set-faces</span>
<span id="cb1-15"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-15" aria-hidden="true" tabindex="-1"></a>   <span class="dt">&#39;user</span></span>
<span id="cb1-16"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-16" aria-hidden="true" tabindex="-1"></a>   `(org-level-1 ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-17"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-17" aria-hidden="true" tabindex="-1"></a>   `(org-level-2 ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-18"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-18" aria-hidden="true" tabindex="-1"></a>   `(org-level-3 ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-19"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-19" aria-hidden="true" tabindex="-1"></a>   `(org-level-4 ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-20"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-20" aria-hidden="true" tabindex="-1"></a>   `(org-level-5 ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-21"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-21" aria-hidden="true" tabindex="-1"></a>   `(org-level-6 ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-22"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-22" aria-hidden="true" tabindex="-1"></a>   `(org-level-7 ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-23"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-23" aria-hidden="true" tabindex="-1"></a>   `(org-level-8 ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-24"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-24" aria-hidden="true" tabindex="-1"></a>   `(org-document-title ((<span class="kw">t</span> (,@headline ,@variable-tuple))))</span>
<span id="cb1-25"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-26"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-26" aria-hidden="true" tabindex="-1"></a>   `(variable-pitch     ((<span class="kw">t</span> ,@variable-tuple)))</span>
<span id="cb1-27"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-27" aria-hidden="true" tabindex="-1"></a>   `(fixed-pitch        ((<span class="kw">t</span> ,@fixed-tuple)))</span>
<span id="cb1-28"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-29"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-29" aria-hidden="true" tabindex="-1"></a>   &#39;(org-ellipsis ((<span class="kw">t</span> (:inherit fixed-pitch :foreground <span class="st">&quot;gray40&quot;</span> :underline <span class="kw">nil</span>))))</span>
<span id="cb1-30"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-30" aria-hidden="true" tabindex="-1"></a>   &#39;(org-block            ((<span class="kw">t</span> (:inherit fixed-pitch))))</span>
<span id="cb1-31"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-31" aria-hidden="true" tabindex="-1"></a>   &#39;(org-block-begin-line ((<span class="kw">t</span> (:inherit fixed-pitch))))</span>
<span id="cb1-32"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-32" aria-hidden="true" tabindex="-1"></a>   &#39;(org-block-end-line   ((<span class="kw">t</span> (:inherit fixed-pitch))))</span>
<span id="cb1-33"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-33" aria-hidden="true" tabindex="-1"></a>   &#39;(org-src              ((<span class="kw">t</span> (:inherit fixed-pitch))))</span>
<span id="cb1-34"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-34" aria-hidden="true" tabindex="-1"></a>   &#39;(org-properties       ((<span class="kw">t</span> (:inherit fixed-pitch))))</span>
<span id="cb1-35"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-35" aria-hidden="true" tabindex="-1"></a>   &#39;(org-code             ((<span class="kw">t</span> (:inherit (<span class="kw">shadow</span> fixed-pitch)))))</span>
<span id="cb1-36"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-36" aria-hidden="true" tabindex="-1"></a>   &#39;(org-date             ((<span class="kw">t</span> (:inherit (<span class="kw">shadow</span> fixed-pitch)))))</span>
<span id="cb1-37"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-37" aria-hidden="true" tabindex="-1"></a>   &#39;(org-document-info    ((<span class="kw">t</span> (:inherit (<span class="kw">shadow</span> fixed-pitch)))))</span>
<span id="cb1-38"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-38" aria-hidden="true" tabindex="-1"></a>   &#39;(org-document-info-keyword ((<span class="kw">t</span> (:inherit (<span class="kw">shadow</span> fixed-pitch)))))</span>
<span id="cb1-39"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-39" aria-hidden="true" tabindex="-1"></a>   &#39;(org-drawer           ((<span class="kw">t</span> (:inherit (<span class="kw">shadow</span> fixed-pitch)))))</span>
<span id="cb1-40"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-40" aria-hidden="true" tabindex="-1"></a>   &#39;(org-indent           ((<span class="kw">t</span> (:inherit (org-hide fixed-pitch)))))</span>
<span id="cb1-41"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-41" aria-hidden="true" tabindex="-1"></a>   `(org-link             ((<span class="kw">t</span> (:inherit fixed-pitch :foreground ,(doom-color <span class="dt">&#39;blue</span>) :underline <span class="kw">t</span>))))</span>
<span id="cb1-42"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-42" aria-hidden="true" tabindex="-1"></a>   &#39;(org-meta-line        ((<span class="kw">t</span> (:inherit (font-lock-comment-face fixed-pitch)))))</span>
<span id="cb1-43"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-43" aria-hidden="true" tabindex="-1"></a>   &#39;(org-property-value   ((<span class="kw">t</span> (:inherit fixed-pitch))) <span class="kw">t</span>)</span>
<span id="cb1-44"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-44" aria-hidden="true" tabindex="-1"></a>   &#39;(org-special-keyword  ((<span class="kw">t</span> (:inherit (font-lock-comment-face fixed-pitch)))))</span>
<span id="cb1-45"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-45" aria-hidden="true" tabindex="-1"></a>   &#39;(org-table            ((<span class="kw">t</span> (:inherit fixed-pitch))))</span>
<span id="cb1-46"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-46" aria-hidden="true" tabindex="-1"></a>   &#39;(org-tag              ((<span class="kw">t</span> (:inherit (<span class="kw">shadow</span> fixed-pitch) :weight bold :height <span class="fl">0.8</span>))))</span>
<span id="cb1-47"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb1-47" aria-hidden="true" tabindex="-1"></a>   &#39;(org-verbatim         ((<span class="kw">t</span> (:inherit (<span class="kw">shadow</span> fixed-pitch)))))))</span></code></pre></div><h2 id="digression-about-why-i-did-that-">Digression about why I did
that;</h2><p>For some reason a went to the rabbit hole of tweaking my emacs. In
fact, it first started as; let's try to switch from <code class="verbatim">doom-emacs</code><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> to <code class="verbatim">nano-emacs</code><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a>. But, doing so, I
realized I wouldn't be able to reach the quality and optimization
provided by doom-emacs myself. So instead of doing this, I first tried
to copy the theme of nano. Then I realized one of the biggest factor of
nano look & feel was its usage of "Roboto Mono" but with weight
light (or Thin).</p><p>See</p><figure><img src="https://yannesposito.com/posts/0020-cool-looking-org-mode/nano-emacs-light.png.webp" title="nano-emacs" alt="GNU Emacs / N Λ N O Look (light theme)"></img><figcaption aria-hidden="true">GNU Emacs / N Λ N O Look (light
theme)</figcaption></figure><figure><img src="https://yannesposito.com/posts/0020-cool-looking-org-mode/nano-emacs-dark.png.webp" title="nano-emacs" alt="GNU Emacs / N Λ N O Look (dark theme)"></img><figcaption aria-hidden="true">GNU Emacs / N Λ N O Look (dark
theme)</figcaption></figure><p>OK so… I just tried to match the theme colors. It was easy to create
a theme with matching colors. <strong>But</strong>, to make it really
looks like nano; almost monochrome with two accent colors; it would mean
a lot more work than anyone could expect. For most emacs mode you
probably need to add a set of specific font faces. This choice is also
what makes nano looks so good too. This is not just about the color, but
about a lot more details than that. Using the good colors only at the
right place is difficult to achieve. And not only the colors, but also,
the correct fonts, the spacing of text elements etc…</p><p>Unfortunately if you want the nano look and feel in doom, it is much
more work than just copying the nano theme.</p><p>But this research of look and feel opened the door to using thin
fonts in emacs. And also tweaking the fonts which really improve the
look & feel of emacs.</p><p>With this conf, I do not use the same font for coding and for writing
prose or a blog post with code blocks. So far, I like this new look and
feel.</p><h2 id="bonuses">Bonuses</h2><h3 id="thin-code-fonts">Thin Code fonts</h3><p>After lot of try, I finally switched my default coding font to
SauceCodePro Nerd Font Mono with weight <code>semi-light</code>. This is
a clone of Adobe <code class="verbatim">SourceCode Pro</code>. And on a
retina display it is really nice to use.</p><div class="sourceCode" id="cb2" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb2-1"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">setq</span> doom-font (font-spec :family <span class="st">&quot;SauceCodePro Nerd Font Mono&quot;</span> :size <span class="dv">12</span> :weight <span class="dt">&#39;semi-light</span>)</span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a>      doom-variable-pitch-font (font-spec :family <span class="st">&quot;iMWritingDuoS Nerd Font&quot;</span> :size <span class="dv">14</span>))</span></code></pre></div><h3 id="an-unfinished-nano-theme-for-doom">An unfinished nano theme for
doom</h3><p>Even though the result is not 100% satisfactory, you could start
using my work. Save this file into <code class="verbatim">~/.doom.d/themes/doom-nano-theme.el</code>:</p><div class="sourceCode" id="cb3" data-org-language="lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb3-1"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">;;; doom-nano-theme.el --- inspired by Nicolas Rougier nano-theme -*- lexical-binding: t; no-byte-compile: t; -*-</span></span>
<span id="cb3-2"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb3-3"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co">;; Author: Yann Esposito &lt;https://yannesposito.com&gt;</span></span>
<span id="cb3-4"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">;; Created: August 16, 2021</span></span>
<span id="cb3-5"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">;; Version: 1.0.0</span></span>
<span id="cb3-6"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co">;; Keywords: custom themes, faces</span></span>
<span id="cb3-7"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="co">;; Homepage: https://github.com/hlissner/emacs-doom-themes</span></span>
<span id="cb3-8"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="co">;; Package-Requires: ((emacs &quot;25.1&quot;) (cl-lib &quot;0.5&quot;) (doom-themes &quot;2.2.1&quot;))</span></span>
<span id="cb3-9"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb3-10"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="co">;;; Commentary:</span></span>
<span id="cb3-11"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb3-12"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="co">;; Ported from nano-theme: https://github.com/rougier/nano-theme</span></span>
<span id="cb3-13"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb3-14"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="co">;;; Code:</span></span>
<span id="cb3-15"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-16"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-16" aria-hidden="true" tabindex="-1"></a>(<span class="kw">require</span> <span class="dt">&#39;doom-themes</span>)</span>
<span id="cb3-17"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-18"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-18" aria-hidden="true" tabindex="-1"></a><span class="co">;;; Variables</span></span>
<span id="cb3-19"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-20"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-20" aria-hidden="true" tabindex="-1"></a>(defgroup doom-plain-theme <span class="kw">nil</span></span>
<span id="cb3-21"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-21" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;Options for the `doom-plain&#39; theme.&quot;</span></span>
<span id="cb3-22"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-22" aria-hidden="true" tabindex="-1"></a>  :group <span class="dt">&#39;doom-themes</span>)</span>
<span id="cb3-23"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-24"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-24" aria-hidden="true" tabindex="-1"></a>(defcustom doom-plain-padded-modeline doom-themes-padded-modeline</span>
<span id="cb3-25"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-25" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;If non-nil, adds a 4px padding to the mode-line.</span></span>
<span id="cb3-26"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-26" aria-hidden="true" tabindex="-1"></a><span class="st">Can be an integer to determine the exact padding.&quot;</span></span>
<span id="cb3-27"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-27" aria-hidden="true" tabindex="-1"></a>  :group <span class="dt">&#39;doom-plain-theme</span></span>
<span id="cb3-28"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-28" aria-hidden="true" tabindex="-1"></a>  <span class="bu">:type</span> &#39;(<span class="kw">or</span> <span class="kw">integer</span> <span class="kw">boolean</span>))</span>
<span id="cb3-29"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-29" aria-hidden="true" tabindex="-1"></a><span class="co">;;</span></span>
<span id="cb3-30"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-30" aria-hidden="true" tabindex="-1"></a><span class="co">;;; Theme definition</span></span>
<span id="cb3-31"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-32"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-32" aria-hidden="true" tabindex="-1"></a>(def-doom-theme doom-nano</span>
<span id="cb3-33"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-33" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;Theme inspired by Nicolas Rougier nano-theme&quot;</span></span>
<span id="cb3-34"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-35"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-35" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; name      default/256/16</span></span>
<span id="cb3-36"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-36" aria-hidden="true" tabindex="-1"></a>  ((nano-color-foreground &#39;(<span class="st">&quot;#37474F&quot;</span>)) <span class="co">;; Blue Grey / L800</span></span>
<span id="cb3-37"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-37" aria-hidden="true" tabindex="-1"></a>   (nano-color-background &#39;(<span class="st">&quot;#FFFFFF&quot;</span>)) <span class="co">;; White</span></span>
<span id="cb3-38"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-38" aria-hidden="true" tabindex="-1"></a>   (nano-color-highlight  &#39;(<span class="st">&quot;#FAFAFA&quot;</span>)) <span class="co">;; Very Light Grey</span></span>
<span id="cb3-39"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-39" aria-hidden="true" tabindex="-1"></a>   (nano-color-critical   &#39;(<span class="st">&quot;#FF6F00&quot;</span>)) <span class="co">;; Amber / L900</span></span>
<span id="cb3-40"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-40" aria-hidden="true" tabindex="-1"></a>   (nano-color-salient    &#39;(<span class="st">&quot;#673AB7&quot;</span>)) <span class="co">;; Deep Purple / L500</span></span>
<span id="cb3-41"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-41" aria-hidden="true" tabindex="-1"></a>   (nano-color-strong     &#39;(<span class="st">&quot;#000000&quot;</span>)) <span class="co">;; Black</span></span>
<span id="cb3-42"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-42" aria-hidden="true" tabindex="-1"></a>   (nano-color-popout     &#39;(<span class="st">&quot;#FFAB91&quot;</span>)) <span class="co">;; Deep Orange / L200</span></span>
<span id="cb3-43"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-43" aria-hidden="true" tabindex="-1"></a>   (nano-color-subtle     &#39;(<span class="st">&quot;#ECEFF1&quot;</span>)) <span class="co">;; Blue Grey / L50</span></span>
<span id="cb3-44"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-44" aria-hidden="true" tabindex="-1"></a>   (nano-color-faded      &#39;(<span class="st">&quot;#B0BEC5&quot;</span>)) <span class="co">;; Blue Grey / L200</span></span>
<span id="cb3-45"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-46"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-46" aria-hidden="true" tabindex="-1"></a>   (bg         nano-color-background)</span>
<span id="cb3-47"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-47" aria-hidden="true" tabindex="-1"></a>   (bg-alt     nano-color-highlight)</span>
<span id="cb3-48"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-48" aria-hidden="true" tabindex="-1"></a>   (base0      &#39;(<span class="st">&quot;#18282f&quot;</span>))</span>
<span id="cb3-49"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-49" aria-hidden="true" tabindex="-1"></a>   (base1      &#39;(<span class="st">&quot;#24323a&quot;</span>))</span>
<span id="cb3-50"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-50" aria-hidden="true" tabindex="-1"></a>   (base2      &#39;(<span class="st">&quot;#556066&quot;</span>))</span>
<span id="cb3-51"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-51" aria-hidden="true" tabindex="-1"></a>   (base3      &#39;(<span class="st">&quot;#6f787d&quot;</span>))</span>
<span id="cb3-52"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-52" aria-hidden="true" tabindex="-1"></a>   (base4      &#39;(<span class="st">&quot;#8a9296&quot;</span>))</span>
<span id="cb3-53"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-53" aria-hidden="true" tabindex="-1"></a>   (base5      &#39;(<span class="st">&quot;#a6acaf&quot;</span>))</span>
<span id="cb3-54"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-54" aria-hidden="true" tabindex="-1"></a>   (base6      &#39;(<span class="st">&quot;#e7e8e9&quot;</span>))</span>
<span id="cb3-55"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-55" aria-hidden="true" tabindex="-1"></a>   (base7      &#39;(<span class="st">&quot;#f6f6f6&quot;</span>))</span>
<span id="cb3-56"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-56" aria-hidden="true" tabindex="-1"></a>   (base8      &#39;(<span class="st">&quot;#fafafa&quot;</span>))</span>
<span id="cb3-57"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-57" aria-hidden="true" tabindex="-1"></a>   (fg         nano-color-foreground)</span>
<span id="cb3-58"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-58" aria-hidden="true" tabindex="-1"></a>   (fg-alt     nano-color-faded)</span>
<span id="cb3-59"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-59" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-60"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-60" aria-hidden="true" tabindex="-1"></a>   (grey       fg)</span>
<span id="cb3-61"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-61" aria-hidden="true" tabindex="-1"></a>   (red        fg)</span>
<span id="cb3-62"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-62" aria-hidden="true" tabindex="-1"></a>   (blue       fg)</span>
<span id="cb3-63"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-63" aria-hidden="true" tabindex="-1"></a>   (dark-blue  fg)</span>
<span id="cb3-64"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-64" aria-hidden="true" tabindex="-1"></a>   (orange     fg)</span>
<span id="cb3-65"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-65" aria-hidden="true" tabindex="-1"></a>   (green      fg)</span>
<span id="cb3-66"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-66" aria-hidden="true" tabindex="-1"></a>   (teal       fg)</span>
<span id="cb3-67"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-67" aria-hidden="true" tabindex="-1"></a>   (yellow     fg)</span>
<span id="cb3-68"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-68" aria-hidden="true" tabindex="-1"></a>   (magenta    fg)</span>
<span id="cb3-69"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-69" aria-hidden="true" tabindex="-1"></a>   (violet     fg)</span>
<span id="cb3-70"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-70" aria-hidden="true" tabindex="-1"></a>   (cyan       fg)</span>
<span id="cb3-71"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-71" aria-hidden="true" tabindex="-1"></a>   (dark-cyan  fg)</span>
<span id="cb3-72"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-72" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-73"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-73" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-74"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-74" aria-hidden="true" tabindex="-1"></a>   <span class="co">;; face categories -- required for all themes</span></span>
<span id="cb3-75"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-75" aria-hidden="true" tabindex="-1"></a>   (highlight      nano-color-salient)</span>
<span id="cb3-76"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-76" aria-hidden="true" tabindex="-1"></a>   (vertical-bar   base5)</span>
<span id="cb3-77"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-77" aria-hidden="true" tabindex="-1"></a>   (selection      nano-color-highlight)</span>
<span id="cb3-78"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-78" aria-hidden="true" tabindex="-1"></a>   (builtin        nano-color-salient)</span>
<span id="cb3-79"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-79" aria-hidden="true" tabindex="-1"></a>   (comments       nano-color-faded)</span>
<span id="cb3-80"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-80" aria-hidden="true" tabindex="-1"></a>   (doc-comments   nano-color-faded)</span>
<span id="cb3-81"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-81" aria-hidden="true" tabindex="-1"></a>   (constants      nano-color-strong)</span>
<span id="cb3-82"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-82" aria-hidden="true" tabindex="-1"></a>   (functions      nano-color-salient)</span>
<span id="cb3-83"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-83" aria-hidden="true" tabindex="-1"></a>   (keywords       nano-color-strong)</span>
<span id="cb3-84"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-84" aria-hidden="true" tabindex="-1"></a>   (methods        nano-color-salient)</span>
<span id="cb3-85"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-85" aria-hidden="true" tabindex="-1"></a>   (operators      nano-color-strong)</span>
<span id="cb3-86"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-86" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">type</span>           nano-color-strong)</span>
<span id="cb3-87"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-87" aria-hidden="true" tabindex="-1"></a>   (strings        base0)</span>
<span id="cb3-88"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-88" aria-hidden="true" tabindex="-1"></a>   (variables      base0)</span>
<span id="cb3-89"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-89" aria-hidden="true" tabindex="-1"></a>   (numbers        base0)</span>
<span id="cb3-90"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-90" aria-hidden="true" tabindex="-1"></a>   (region         base4)</span>
<span id="cb3-91"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-91" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">error</span>          nano-color-critical)</span>
<span id="cb3-92"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-92" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">warning</span>        nano-color-popout)</span>
<span id="cb3-93"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-93" aria-hidden="true" tabindex="-1"></a>   (success        nano-color-salient)</span>
<span id="cb3-94"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-94" aria-hidden="true" tabindex="-1"></a>   (vc-modified    nano-color-salient)</span>
<span id="cb3-95"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-95" aria-hidden="true" tabindex="-1"></a>   (vc-added       fg-alt)</span>
<span id="cb3-96"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-96" aria-hidden="true" tabindex="-1"></a>   (vc-deleted     nano-color-critical)</span>
<span id="cb3-97"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-97" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-98"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-98" aria-hidden="true" tabindex="-1"></a>   <span class="co">;; custom categories</span></span>
<span id="cb3-99"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-99" aria-hidden="true" tabindex="-1"></a>   (-modeline-pad</span>
<span id="cb3-100"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-100" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">when</span> doom-plain-padded-modeline</span>
<span id="cb3-101"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-101" aria-hidden="true" tabindex="-1"></a>      (<span class="kw">if</span> (<span class="kw">integerp</span> doom-plain-padded-modeline) doom-plain-padded-modeline <span class="dv">4</span>)))</span>
<span id="cb3-102"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-102" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-103"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-103" aria-hidden="true" tabindex="-1"></a>   (modeline-bg              (doom-darken bg-alt <span class="fl">0.15</span>))</span>
<span id="cb3-104"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-104" aria-hidden="true" tabindex="-1"></a>   (modeline-bg-alt          (doom-darken bg-alt <span class="fl">0.1</span>))</span>
<span id="cb3-105"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-105" aria-hidden="true" tabindex="-1"></a>   (modeline-bg-inactive     (doom-darken bg-alt <span class="fl">0.1</span>))</span>
<span id="cb3-106"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-106" aria-hidden="true" tabindex="-1"></a>   (modeline-bg-inactive-alt bg-alt)</span>
<span id="cb3-107"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-107" aria-hidden="true" tabindex="-1"></a>   (modeline-fg              fg)</span>
<span id="cb3-108"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-108" aria-hidden="true" tabindex="-1"></a>   (modeline-fg-alt          (doom-darken modeline-bg-inactive <span class="fl">0.35</span>)))</span>
<span id="cb3-109"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-109" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-110"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-110" aria-hidden="true" tabindex="-1"></a>  <span class="co">;;;; Base theme face overrides</span></span>
<span id="cb3-111"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-111" aria-hidden="true" tabindex="-1"></a>  ((<span class="kw">error</span>   :underline `(:style wave :color ,<span class="kw">error</span>))</span>
<span id="cb3-112"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-112" aria-hidden="true" tabindex="-1"></a>   (<span class="kw">warning</span> :underline `(:style wave :color ,<span class="kw">warning</span>))</span>
<span id="cb3-113"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-113" aria-hidden="true" tabindex="-1"></a>   ((font-lock-constant-face &amp;override)      :slant <span class="dt">&#39;italic</span>)</span>
<span id="cb3-114"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-114" aria-hidden="true" tabindex="-1"></a>   ((font-lock-comment-face &amp;override)       :slant <span class="dt">&#39;italic</span>)</span>
<span id="cb3-115"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-115" aria-hidden="true" tabindex="-1"></a>   ((font-lock-function-name-face &amp;override) :slant <span class="dt">&#39;italic</span>)</span>
<span id="cb3-116"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-116" aria-hidden="true" tabindex="-1"></a>   ((font-lock-type-face &amp;override)          :slant <span class="dt">&#39;italic</span>)</span>
<span id="cb3-117"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-117" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;(hl-line :background base8)</span></span>
<span id="cb3-118"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-118" aria-hidden="true" tabindex="-1"></a>   ((line-number &amp;override) :foreground base3)</span>
<span id="cb3-119"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-119" aria-hidden="true" tabindex="-1"></a>   ((line-number-current-line &amp;override) :foreground base2)</span>
<span id="cb3-120"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-120" aria-hidden="true" tabindex="-1"></a>   (mode-line</span>
<span id="cb3-121"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-121" aria-hidden="true" tabindex="-1"></a>    :background modeline-bg :foreground modeline-fg</span>
<span id="cb3-122"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-122" aria-hidden="true" tabindex="-1"></a>    :box (<span class="kw">if</span> -modeline-pad `(:line-width ,-modeline-pad :color ,modeline-bg)))</span>
<span id="cb3-123"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-123" aria-hidden="true" tabindex="-1"></a>   (mode-line-inactive</span>
<span id="cb3-124"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-124" aria-hidden="true" tabindex="-1"></a>    :background modeline-bg-inactive :foreground modeline-fg-alt</span>
<span id="cb3-125"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-125" aria-hidden="true" tabindex="-1"></a>    :box (<span class="kw">if</span> -modeline-pad `(:line-width ,-modeline-pad :color ,modeline-bg-inactive)))</span>
<span id="cb3-126"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-126" aria-hidden="true" tabindex="-1"></a>   (mode-line-emphasis :foreground highlight)</span>
<span id="cb3-127"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-127" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-128"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-128" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; doom-modeline</span></span>
<span id="cb3-129"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-129" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-bar :background modeline-bg)</span>
<span id="cb3-130"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-130" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-bar-inactive :inherit <span class="dt">&#39;doom-modeline-bar</span>)</span>
<span id="cb3-131"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-131" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-project-dir :foreground fg)</span>
<span id="cb3-132"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-132" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-buffer-file :foreground fg)</span>
<span id="cb3-133"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-133" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-buffer-modified :weight <span class="dt">&#39;bold</span> :foreground <span class="st">&quot;#000000&quot;</span>)</span>
<span id="cb3-134"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-134" aria-hidden="true" tabindex="-1"></a>   (doom-modeline-panel :inherit <span class="dt">&#39;mode-line-highlight</span> :background base3 :foreground fg)</span>
<span id="cb3-135"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-135" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; ivy</span></span>
<span id="cb3-136"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-136" aria-hidden="true" tabindex="-1"></a>   (ivy-posframe :background bg-alt)</span>
<span id="cb3-137"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-137" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; magit</span></span>
<span id="cb3-138"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-138" aria-hidden="true" tabindex="-1"></a>   ((magit-diff-added-highlight &amp;override)   :foreground fg :background (doom-blend vc-added bg <span class="fl">0.3</span>))</span>
<span id="cb3-139"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-139" aria-hidden="true" tabindex="-1"></a>   ((magit-diff-removed &amp;override)           :foreground (doom-lighten fg <span class="fl">0.4</span>) :background (doom-blend vc-deleted bg <span class="fl">0.1</span>))</span>
<span id="cb3-140"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-140" aria-hidden="true" tabindex="-1"></a>   ((magit-diff-removed-highlight &amp;override) :foreground fg :background (doom-blend vc-deleted bg <span class="fl">0.22</span>))</span>
<span id="cb3-141"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-141" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; lsp-mode</span></span>
<span id="cb3-142"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-142" aria-hidden="true" tabindex="-1"></a>   (lsp-headerline-breadcrumb-symbols-face :foreground keywords :weight <span class="dt">&#39;bold</span>)</span>
<span id="cb3-143"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-143" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; outline &lt;built-in&gt;</span></span>
<span id="cb3-144"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-144" aria-hidden="true" tabindex="-1"></a>   (outline-1 :slant   <span class="dt">&#39;italic</span> :foreground fg-alt)</span>
<span id="cb3-145"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-145" aria-hidden="true" tabindex="-1"></a>   (outline-2 :inherit <span class="dt">&#39;outline-1</span> :foreground base2)</span>
<span id="cb3-146"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-146" aria-hidden="true" tabindex="-1"></a>   (outline-3 :inherit <span class="dt">&#39;outline-2</span>)</span>
<span id="cb3-147"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-147" aria-hidden="true" tabindex="-1"></a>   (outline-4 :inherit <span class="dt">&#39;outline-3</span>)</span>
<span id="cb3-148"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-148" aria-hidden="true" tabindex="-1"></a>   (outline-5 :inherit <span class="dt">&#39;outline-4</span>)</span>
<span id="cb3-149"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-149" aria-hidden="true" tabindex="-1"></a>   (outline-6 :inherit <span class="dt">&#39;outline-5</span>)</span>
<span id="cb3-150"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-150" aria-hidden="true" tabindex="-1"></a>   (outline-7 :inherit <span class="dt">&#39;outline-6</span>)</span>
<span id="cb3-151"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-151" aria-hidden="true" tabindex="-1"></a>   (outline-8 :inherit <span class="dt">&#39;outline-7</span>)</span>
<span id="cb3-152"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-152" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-153"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-153" aria-hidden="true" tabindex="-1"></a>   (org-level-1 :inherit <span class="dt">&#39;org-level-1</span> :foreground nano-color-strong)</span>
<span id="cb3-154"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-154" aria-hidden="true" tabindex="-1"></a>   (org-level-2 :inherit <span class="dt">&#39;org-level-2</span> :foreground nano-color-strong)</span>
<span id="cb3-155"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-155" aria-hidden="true" tabindex="-1"></a>   (org-level-3 :inherit <span class="dt">&#39;org-level-3</span> :foreground nano-color-strong)</span>
<span id="cb3-156"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-156" aria-hidden="true" tabindex="-1"></a>   (org-level-4 :inherit <span class="dt">&#39;org-level-4</span> :foreground nano-color-strong)</span>
<span id="cb3-157"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-157" aria-hidden="true" tabindex="-1"></a>   (org-level-5 :inherit <span class="dt">&#39;org-level-5</span> :foreground nano-color-strong)</span>
<span id="cb3-158"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-158" aria-hidden="true" tabindex="-1"></a>   (org-level-6 :inherit <span class="dt">&#39;org-level-6</span> :foreground nano-color-strong)</span>
<span id="cb3-159"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-159" aria-hidden="true" tabindex="-1"></a>   (org-level-7 :inherit <span class="dt">&#39;org-level-7</span> :foreground nano-color-strong)</span>
<span id="cb3-160"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-160" aria-hidden="true" tabindex="-1"></a>   (org-level-8 :inherit <span class="dt">&#39;org-level-8</span> :foreground nano-color-strong)</span>
<span id="cb3-161"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-161" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-162"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-162" aria-hidden="true" tabindex="-1"></a>   (org-code     :inherit <span class="dt">&#39;org-code</span></span>
<span id="cb3-163"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-163" aria-hidden="true" tabindex="-1"></a>                 :foreground nano-color-salient</span>
<span id="cb3-164"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-164" aria-hidden="true" tabindex="-1"></a>                 :weight <span class="dt">&#39;bold</span>)</span>
<span id="cb3-165"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-165" aria-hidden="true" tabindex="-1"></a>   (org-verbatim :inherit <span class="dt">&#39;org-verbatim</span></span>
<span id="cb3-166"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-166" aria-hidden="true" tabindex="-1"></a>                 :foreground nano-color-salient</span>
<span id="cb3-167"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-167" aria-hidden="true" tabindex="-1"></a>                 :weight <span class="dt">&#39;bold</span>)</span>
<span id="cb3-168"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-168" aria-hidden="true" tabindex="-1"></a>   (org-upcoming-deadline :inherit <span class="dt">&#39;org-upcoming-deadline</span></span>
<span id="cb3-169"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-169" aria-hidden="true" tabindex="-1"></a>                          :foreground nano-color-critical</span>
<span id="cb3-170"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-170" aria-hidden="true" tabindex="-1"></a>                          :weight <span class="dt">&#39;bold</span>)</span>
<span id="cb3-171"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-171" aria-hidden="true" tabindex="-1"></a>   (org-upcoming-distant-deadline :inherit <span class="dt">&#39;org-upcoming-distant-deadline</span></span>
<span id="cb3-172"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-172" aria-hidden="true" tabindex="-1"></a>                                  :foreground nano-color-salient)</span>
<span id="cb3-173"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-173" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-174"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-174" aria-hidden="true" tabindex="-1"></a>   (org-habit-overdue-face</span>
<span id="cb3-175"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-175" aria-hidden="true" tabindex="-1"></a>    :inherit <span class="dt">&#39;org-habit-overdue-face</span></span>
<span id="cb3-176"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-176" aria-hidden="true" tabindex="-1"></a>    :background fg-alt)</span>
<span id="cb3-177"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-177" aria-hidden="true" tabindex="-1"></a>   (org-habit-overdue-future-face</span>
<span id="cb3-178"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-178" aria-hidden="true" tabindex="-1"></a>    :inherit <span class="dt">&#39;org-habit-overdue-future-face</span></span>
<span id="cb3-179"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-179" aria-hidden="true" tabindex="-1"></a>    :background nano-color-subtle)</span>
<span id="cb3-180"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-180" aria-hidden="true" tabindex="-1"></a>   (org-habit-alert-face</span>
<span id="cb3-181"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-181" aria-hidden="true" tabindex="-1"></a>    :inherit <span class="dt">&#39;org-habit-alert-face</span></span>
<span id="cb3-182"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-182" aria-hidden="true" tabindex="-1"></a>    :background nano-color-critical)</span>
<span id="cb3-183"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-183" aria-hidden="true" tabindex="-1"></a>   (org-habit-alert-future-face</span>
<span id="cb3-184"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-184" aria-hidden="true" tabindex="-1"></a>    :inherit <span class="dt">&#39;org-habit-alert-future-face</span></span>
<span id="cb3-185"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-185" aria-hidden="true" tabindex="-1"></a>    :background nano-color-subtle)</span>
<span id="cb3-186"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-186" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-187"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-187" aria-hidden="true" tabindex="-1"></a>   (org-scheduled-today :inherit <span class="dt">&#39;org-scheduled-today</span> :foreground fg)</span>
<span id="cb3-188"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-188" aria-hidden="true" tabindex="-1"></a>   (org-scheduled-previously :inherit <span class="dt">&#39;org-scheduled-previously</span> :foreground fg)</span>
<span id="cb3-189"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-189" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-190"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-190" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; org &lt;built-in&gt;</span></span>
<span id="cb3-191"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-191" aria-hidden="true" tabindex="-1"></a>   ((org-block &amp;override) :background bg-alt)</span>
<span id="cb3-192"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-192" aria-hidden="true" tabindex="-1"></a>   ((org-block-begin-line &amp;override) :foreground base5)</span>
<span id="cb3-193"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-193" aria-hidden="true" tabindex="-1"></a>   <span class="co">;;;; solaire-mode</span></span>
<span id="cb3-194"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-194" aria-hidden="true" tabindex="-1"></a>   (solaire-mode-line-face</span>
<span id="cb3-195"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-195" aria-hidden="true" tabindex="-1"></a>    :inherit <span class="dt">&#39;mode-line</span></span>
<span id="cb3-196"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-196" aria-hidden="true" tabindex="-1"></a>    :background modeline-bg-alt</span>
<span id="cb3-197"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-197" aria-hidden="true" tabindex="-1"></a>    :box (<span class="kw">if</span> -modeline-pad `(:line-width ,-modeline-pad :color ,modeline-bg-alt)))</span>
<span id="cb3-198"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-198" aria-hidden="true" tabindex="-1"></a>   (solaire-mode-line-inactive-face</span>
<span id="cb3-199"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-199" aria-hidden="true" tabindex="-1"></a>    :inherit <span class="dt">&#39;mode-line-inactive</span></span>
<span id="cb3-200"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-200" aria-hidden="true" tabindex="-1"></a>    :background modeline-bg-inactive-alt</span>
<span id="cb3-201"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-201" aria-hidden="true" tabindex="-1"></a>    :box (<span class="kw">if</span> -modeline-pad `(:line-width ,-modeline-pad :color ,modeline-bg-inactive-alt)))))</span>
<span id="cb3-202"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-202" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-203"><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#cb3-203" aria-hidden="true" tabindex="-1"></a><span class="co">;;; doom-plain-theme.el ends here</span></span></code></pre></div><p>You will probably need more work to achieve the colors you expect.
For that, using <code>SPC-u C-x =</code> will probably be useful. It
will show the font face under the cursor.</p><p>Best of luck.</p><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr></hr><ol><li id="fn1"><p><a href="https://www.nerdfonts.com">https://www.nerdfonts.com</a><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p><a href="https://www.nerdfonts.com">https://www.nerdfonts.com</a><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn3"><p><a href="https://github.com/hlissner/doom-emacs">https://github.com/hlissner/doom-emacs</a><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn4"><p><a href="https://github.com/rougier/nano-emacs">https://github.com/rougier/nano-emacs</a><a href="https://yannesposito.com/posts/0020-cool-looking-org-mode/index.html#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section></article></main>
]]></description>
</item>


<item>
<title>Utopia (2013)</title>
<guid>https://yannesposito.com/posts/0019-utopia-tv-show/index.html</guid>
<pubDate>Tue, 01 Jun 2021 00:00:00 +0200</pubDate>
<category>tv-show</category>
<description><![CDATA[
<main id="content"><article><figure><img src="https://yannesposito.com/posts/0019-utopia-tv-show/utopia-s01.jpg.webp" alt="Utopia"></img><figcaption aria-hidden="true">Utopia</figcaption></figure><p>I wanted to write a few articles about great shows lot of people
around me do not know about. <a href="https://www.themoviedb.org/tv/46511-utopia">Utopia (2013)</a> is
one of these TV Shows that deserve more attention.</p><p>The <strong>filmography</strong> is quite original. I have never seen
another film/TV show with a similar atmosphere. The usage of bright
colors is magistral. Flashy yellow, green, red.</p><p>The <strong>soundtrack</strong> is also pretty surprising original
and enjoyable.</p><p>The <strong>acting</strong> is really great. Actors are doing a great
job and are quite relatable. These are not the (super)heroes you are
used to. We are far away from shows where every girl is a bimbo and
every guy is a Chippendale.</p><p>The <strong>scenario</strong>, in regard to the recent events related
to COVID19 is just perfect. I do not want to reveal too much. But let's
just say the current real events are close to the events predicted in
this show.</p><p>The <strong>Surrealistic Humor</strong> atmosphere make the viewing
experience quite exceptionnal. There is a mix between nonchalance and
extreme violence. It really feels surrealist. In this show some people
act with extreme violence as if there is no choice.</p><p>As a conclusion, if you are looking for a very innovative TV show
then search no further this one is a great original choice. If you are
still unsure, just watch the opening scene, it is quite incredible.</p><p>ps: Also try to get the original content. Amazon Prime apparently cut
some very important scene and also changed the ratio which hurt the very
good image work.</p></article></main>
]]></description>
</item>


<item>
<title>Fast Static Site with make</title>
<guid>https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html</guid>
<pubDate>Tue, 25 May 2021 00:00:00 +0200</pubDate>
<category>blog</category>
<category>static</category>
<description><![CDATA[
<main id="content"><article><p>This article will dig a bit deeper about my <code class="verbatim">Makefile</code> based static website generator. In a <a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html">previous
article</a> I just gave the rationale and an overview to do it yourself.
Mainly it is very fast and portable.</p><p>A few goals reached by my current build system are:</p><ol><li>Be fast and make the minimal amount of work as possible. I don't
want to rebuild all the html pages if I only change one file.</li><li>Source file format agnostic. You can use markdown, org-mode or even
directly write html.</li><li>Support gemini</li><li>Optimize size: minify HTML, CSS, images</li><li>Generate an index page listing the posts</li><li>Generate RSS/atom feed (for both gemini and http)</li></ol><p><code class="verbatim">make</code> will take care of handling the
dependency graph to minimize the amount of effort when a change occurs
in the sources. For some features, I built specifics small shell
scripts. For example to be absolutely agnostic in the source format for
my articles I generate the RSS out of a tree of HTML files. But taking
advantage of <code class="verbatim">make</code>, I generate an index
cache to transform those HTML into XML which will be faster to use to
build different indexes. To make those transformations I use very short
a shell scripts.</p><h1 id="-makefile--overview"><code class="verbatim">Makefile</code>
overview</h1><p>A Makefile is made out of rules. The first rule of your Makefile will
be the default rule. The first rule of my Makefile is called <code class="verbatim">all</code>.</p><p>A rule as the following format:</p><div class="sourceCode" id="cb1"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb1-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dv">target:</span><span class="dt"> file1 file2</span></span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a>    cmd --input file1 file2 <span class="ch">\</span></span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a>        --output target</span></code></pre></div><p>if <code class="verbatim">target</code> does not exists, then <code class="verbatim">make</code> will look at its dependencies. If any of
its dependencies need to be updated, it will run all the rules in the
correct order to rebuild them and finally run the script to build <code class="verbatim">target</code>. A file needs to be updated if one of its
dependency needs to be updated or is newer.</p><p>The usual use case of <code class="verbatim">make</code> is about
building a single binary out of many source files. But for a static
website, we need to generate a lot of files from a lot of files. So we
construct the rules like this:</p><div class="sourceCode" id="cb2"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb2-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dv">all:</span><span class="dt"> site</span></span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="co"># build a list of files that will need to be build</span></span>
<span id="cb2-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_FILES </span><span class="ch">:=</span><span class="st"> ....</span></span>
<span id="cb2-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co"># RULES TO GENERATE DST_FILES</span></span>
<span id="cb2-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_FILES</span><span class="ch">)</span></span>
<span id="cb2-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="co"># another list of files</span></span>
<span id="cb2-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_FILES_2 </span><span class="ch">:=</span><span class="st"> ....</span></span>
<span id="cb2-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co"># RULES TO GENERATE DST_FILES_2</span></span>
<span id="cb2-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_FILES_2</span><span class="ch">)</span></span>
<span id="cb2-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="dv">site:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">ALL</span><span class="ch">)</span></span></code></pre></div><p>In my <code class="verbatim">Makefile</code> I have many similar
block with the same pattern.</p><ol><li>I retrieve a list of source files</li><li>I construct the list of destination files (change the directory, the
extension)</li><li>I declare a rule to construct these destination files</li><li>I add the destination files to the <code class="verbatim">ALL</code>
variable.</li></ol><p>I have a block for:</p><ul><li>raw assets I just want copied</li><li>images I would like to compress for the web</li><li><code class="verbatim">html</code> I would like to generate from org
mode files via pandoc</li><li><code class="verbatim">gmi</code> I would like to generate from org
mode files</li><li><code class="verbatim">xml</code> files I use as cache to build
different index files</li><li><code class="verbatim">index.html</code> file containing a list of
my posts</li><li><code class="verbatim">rss.xml</code> file containing a list of my
posts</li><li><code class="verbatim">gemini-atom.xml</code> file containing a list
of my posts</li></ul><h2 id="assets">Assets</h2><p>The rules to copy assets will be a good first example.</p><ol><li>find all assets in <code class="verbatim">src/</code> directory</li><li>generate all assets from these files in <code class="verbatim">_site/</code> directory</li><li>make this rule a dependency on the <code class="verbatim">all</code>
rule.</li></ol><div class="sourceCode" id="cb3"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb3-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_ASSETS </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> find src -type f</span><span class="ch">)</span></span>
<span id="cb3-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_ASSETS </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> src/%</span><span class="kw">,</span><span class="st">_site/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">SRC_ASSETS</span><span class="ch">))</span></span>
<span id="cb3-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="dv">_site/% :</span><span class="dt"> src/%</span></span>
<span id="cb3-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="st">&quot;</span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span><span class="st">&quot;</span></span>
<span id="cb3-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb3-5" aria-hidden="true" tabindex="-1"></a>    cp <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb3-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> assets</span></span>
<span id="cb3-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="dv">assets:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_ASSETS</span><span class="ch">)</span></span>
<span id="cb3-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> assets</span></span></code></pre></div><p>OK, this looks terrible. But mainly:</p><ul><li><code>SRC_ASSETS</code> will contain the result of the command
<code>find</code>.</li><li><code>DST_ASSETS</code> will contain the files of
<code>SRC_ASSETS</code> but we replace the <code class="verbatim">src/</code> by <code class="verbatim">_site/</code>.</li><li>We create a generic rule; for all files matching the following
pattern <code class="verbatim">_site/%</code>, look for the file <code class="verbatim">src/%</code> and if it is newer (in our case) then
execute the following commands:<ul><li>create the directory to put <code class="verbatim">_site/%</code>
in</li><li>copy the file</li></ul></li></ul><p>About the line <code>@mkdir -p "$(dir $@)"</code>:</p><ul><li>the <code class="verbatim">@</code> at the start of the command
simply means that we make this execution silent.</li><li>The <code class="verbatim">$@</code> is replaced by the target
string.</li><li>And <code class="verbatim">$(dir $@)</code> will generate the folder
name of <code class="verbatim">$@</code>.</li></ul><p>For the line with <code>cp</code>, you just need to know that <code class="verbatim">~$&lt;~</code> will represent the first dependency.</p><p>My Makefile is composed of similar blocks, where I replace the first
find command to match specific files and where I use different building
rules. An important point is that the rules must be the most specific
possible. This is because <code class="verbatim">make</code> will use
the most specific rule in case of ambiguity. For example, the matching
rule <code class="verbatim">_site/%: src/%</code> will match all files
in the <code class="verbatim">src/</code> dir. But if we want to treat
<code class="verbatim">CSS</code> files with another rule we could
write:</p><div class="sourceCode" id="cb4"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb4-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dv">_site/%.css:</span><span class="dt"> src/%.css</span></span>
<span id="cb4-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb4-2" aria-hidden="true" tabindex="-1"></a>    minify <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span></code></pre></div><p>And if the selected file is a <code class="verbatim">CSS</code> file,
this rule will be selected.</p><h2 id="prelude">Prelude</h2><p>I start with variables declarations:</p><div class="sourceCode" id="cb5"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb5-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dv">all:</span><span class="dt"> site</span></span>
<span id="cb5-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="co"># directory containing the source files</span></span>
<span id="cb5-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_DIR </span><span class="ch">?=</span><span class="st"> src</span></span>
<span id="cb5-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co"># directory that will contain the site files</span></span>
<span id="cb5-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_DIR </span><span class="ch">?=</span><span class="st"> _site</span></span>
<span id="cb5-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="co"># a directory that will contain a cache to speedup indexing</span></span>
<span id="cb5-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="dt">CACHE_DIR </span><span class="ch">?=</span><span class="st"> .cache</span></span>
<span id="cb5-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="co"># options to pass to find to prevent matching files in the src/drafts</span></span>
<span id="cb5-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co"># directory</span></span>
<span id="cb5-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="dt">NO_DRAFT </span><span class="ch">:=</span><span class="st"> -not -path &#39;</span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st">/drafts/*&#39;</span></span>
<span id="cb5-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="co"># option to pass to find to not match  org files</span></span>
<span id="cb5-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="dt">NO_SRC_FILE </span><span class="ch">:=</span><span class="st"> ! -name &#39;*.org&#39;</span></span></code></pre></div><h2 id="css">CSS</h2><p>Here we go; the same simple pattern for CSS files.</p><div class="sourceCode" id="cb6"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb6-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co"># CSS</span></span>
<span id="cb6-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_CSS_FILES </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> find </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st"> -type f -name &#39;*.css&#39;</span><span class="ch">)</span></span>
<span id="cb6-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_CSS_FILES </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">SRC_RAW_FILES</span><span class="ch">))</span></span>
<span id="cb6-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/%.css :</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%.css</span></span>
<span id="cb6-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-5" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="st">&quot;</span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span><span class="st">&quot;</span></span>
<span id="cb6-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-6" aria-hidden="true" tabindex="-1"></a>    minify <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> &gt; <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb6-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> css</span></span>
<span id="cb6-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="dv">css:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_CSS_FILES</span><span class="ch">)</span></span>
<span id="cb6-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb6-9" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> css</span></span></code></pre></div><p>This is very similar to the block for raw assets. The difference is
just that instead of using <code class="verbatim">cp</code> we use the
<code class="verbatim">minify</code> command.</p><h2 id="org----html">ORG → HTML</h2><p>Now this one is more complex but is still follow the same
pattern.</p><div class="sourceCode" id="cb7"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb7-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co"># ORG -&gt; HTML</span></span>
<span id="cb7-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="dt">EXT </span><span class="ch">?=</span><span class="st"> .org</span></span>
<span id="cb7-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_PANDOC_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> find </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st"> -type f -name &quot;*</span><span class="ch">$(</span><span class="dt">EXT</span><span class="ch">)</span><span class="st">&quot; </span><span class="ch">$(</span><span class="dt">NO_DRAFT</span><span class="ch">))</span></span>
<span id="cb7-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_PANDOC_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> %</span><span class="ch">$(</span><span class="dt">EXT</span><span class="ch">)</span><span class="kw">,</span><span class="st">%.html</span><span class="kw">,</span><span class="st"> \</span></span>
<span id="cb7-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="st">                        </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="st"> \</span></span>
<span id="cb7-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="st">                            </span><span class="ch">$(</span><span class="dt">SRC_PANDOC_FILES</span><span class="ch">)))</span></span>
<span id="cb7-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="dt">PANDOC_TEMPLATE </span><span class="ch">?=</span><span class="st"> templates/post.html</span></span>
<span id="cb7-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-8" aria-hidden="true" tabindex="-1"></a><span class="dt">MK_HTML </span><span class="ch">:=</span><span class="st"> engine/mk-html.sh</span></span>
<span id="cb7-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="dt">PANDOC </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">MK_HTML</span><span class="ch">)</span><span class="st"> </span><span class="ch">$(</span><span class="dt">PANDOC_TEMPLATE</span><span class="ch">)</span></span>
<span id="cb7-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/%.html:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%.org </span><span class="ch">$(</span><span class="dt">PANDOC_TEMPLATE</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">MK_HTML</span><span class="ch">)</span></span>
<span id="cb7-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-11" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="st">&quot;</span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span><span class="st">&quot;</span></span>
<span id="cb7-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-12" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">PANDOC</span><span class="ch">)</span> <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="ch">$@</span><span class="st">.tmp&quot;</span></span>
<span id="cb7-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-13" aria-hidden="true" tabindex="-1"></a>    minify --mime text/html <span class="st">&quot;</span><span class="ch">$@</span><span class="st">.tmp&quot;</span> &gt; <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb7-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-14" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">rm </span><span class="st">&quot;</span><span class="ch">$@</span><span class="st">.tmp&quot;</span></span>
<span id="cb7-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-15" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> html</span></span>
<span id="cb7-16"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-16" aria-hidden="true" tabindex="-1"></a><span class="dv">html:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_PANDOC_FILES</span><span class="ch">)</span></span>
<span id="cb7-17"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb7-17" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> html</span></span></code></pre></div><p>So to construct <code class="verbatim">DST_PANDOC_FILES</code> this
time we also need to change the extension of the file from <code class="verbatim">org</code> to <code class="verbatim">html</code>. We
need to provide a template that will be passed to pandoc.</p><p>And of course, as if we change the template file we would like to
regenerate all HTML files we put the template as a dependency. But
importantly <strong>not</strong> at the first place. Because we use
<code class="verbatim">$&lt;</code> that will be the first
dependency.</p><p>I also have a short script instead of directly using <code class="verbatim">pandoc</code>. It is easier to handle <code class="verbatim">toc</code> using the metadatas in the file. And if
someday I want to put the template in the metas, this will be the right
place to put that.</p><p>The <code class="verbatim">mk-html.sh</code> is quite
straightforward:</p><div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env bash</span></span>
<span id="cb8-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="bu">set</span> <span class="at">-eu</span></span>
<span id="cb8-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="co"># put me at the top level of my project (like Makefile)</span></span>
<span id="cb8-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">git</span> rev-parse <span class="at">--show-toplevel</span><span class="va">)</span><span class="st">&quot;</span> <span class="kw">||</span> <span class="bu">exit</span> 1</span>
<span id="cb8-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="va">template</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$1</span><span class="st">&quot;</span></span>
<span id="cb8-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="va">orgfile</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$2</span><span class="st">&quot;</span></span>
<span id="cb8-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-8" aria-hidden="true" tabindex="-1"></a><span class="va">htmlfile</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$3</span><span class="st">&quot;</span></span>
<span id="cb8-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="co"># check if there is the #+OPTIONS: toc:t</span></span>
<span id="cb8-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-11" aria-hidden="true" tabindex="-1"></a><span class="va">tocoption</span><span class="op">=</span><span class="st">&quot;&quot;</span></span>
<span id="cb8-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-12" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="fu">grep</span> <span class="at">-ie</span> <span class="st">&#39;^#+options:&#39;</span> <span class="st">&quot;</span><span class="va">$orgfile</span><span class="st">&quot;</span> <span class="kw">|</span> <span class="fu">grep</span> <span class="st">&#39;toc:t&#39;</span><span class="op">&gt;</span>/dev/null<span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb8-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-13" aria-hidden="true" tabindex="-1"></a>    <span class="va">tocoption</span><span class="op">=</span><span class="st">&quot;--toc&quot;</span></span>
<span id="cb8-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-14" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb8-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-16"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-16" aria-hidden="true" tabindex="-1"></a><span class="bu">set</span> <span class="at">-x</span></span>
<span id="cb8-17"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-17" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> <span class="va">$tocoption</span> <span class="dt">\</span></span>
<span id="cb8-18"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-18" aria-hidden="true" tabindex="-1"></a>       <span class="at">--template</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$template</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb8-19"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-19" aria-hidden="true" tabindex="-1"></a>       <span class="at">--mathml</span> <span class="dt">\</span></span>
<span id="cb8-20"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-20" aria-hidden="true" tabindex="-1"></a>       <span class="at">--from</span> org <span class="dt">\</span></span>
<span id="cb8-21"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-21" aria-hidden="true" tabindex="-1"></a>       <span class="at">--to</span> html5 <span class="dt">\</span></span>
<span id="cb8-22"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-22" aria-hidden="true" tabindex="-1"></a>       <span class="at">--standalone</span> <span class="dt">\</span></span>
<span id="cb8-23"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-23" aria-hidden="true" tabindex="-1"></a>       <span class="va">$orgfile</span> <span class="dt">\</span></span>
<span id="cb8-24"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb8-24" aria-hidden="true" tabindex="-1"></a>       <span class="at">--output</span> <span class="st">&quot;</span><span class="va">$htmlfile</span><span class="st">&quot;</span></span></code></pre></div><p>Once generated I also minify the html file. And, that's it. But the
important part is that now, if I change my script or the template or the
file, it will generate the dependencies.</p><h2 id="indexes">Indexes</h2><p>We often need indexes to build a website. Typically to list the
latest articles, build the RSS file. So for sake of simplicity, I
decided to build my index as a set of XML files. Of course, this could
be optimizide, by using SQLite for example. But this will already be
really fast.</p><p>For every generated html file I will generate a clean XML file with
<code class="verbatim">hxclean</code>. Once cleaned, it will be easy to
access a specific node of in these XML files.</p><div class="sourceCode" id="cb9"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb9-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="co"># INDEXES</span></span>
<span id="cb9-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_POSTS_DIR </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st">/posts</span></span>
<span id="cb9-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_POSTS_DIR </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/posts</span></span>
<span id="cb9-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_POSTS_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> find </span><span class="ch">$(</span><span class="dt">SRC_POSTS_DIR</span><span class="ch">)</span><span class="st"> -type f -name &quot;*</span><span class="ch">$(</span><span class="dt">EXT</span><span class="ch">)</span><span class="st">&quot;</span><span class="ch">)</span></span>
<span id="cb9-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="dt">RSS_CACHE_DIR </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">CACHE_DIR</span><span class="ch">)</span><span class="st">/rss</span></span>
<span id="cb9-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-6" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_XML_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> %.org</span><span class="kw">,</span><span class="st">%.xml</span><span class="kw">,</span><span class="st"> \</span></span>
<span id="cb9-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="st">                        </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_POSTS_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">RSS_CACHE_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="st"> \</span></span>
<span id="cb9-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="st">                            </span><span class="ch">$(</span><span class="dt">SRC_POSTS_FILES</span><span class="ch">)))</span></span>
<span id="cb9-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-9" aria-hidden="true" tabindex="-1"></a><span class="dv">$(RSS_CACHE_DIR)/%.xml:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_POSTS_DIR</span><span class="ch">)</span><span class="dt">/%.html</span></span>
<span id="cb9-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-10" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="st">&quot;</span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span><span class="st">&quot;</span></span>
<span id="cb9-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-11" aria-hidden="true" tabindex="-1"></a>    hxclean <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> &gt; <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb9-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> indexcache</span></span>
<span id="cb9-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-13" aria-hidden="true" tabindex="-1"></a><span class="dv">indexcache:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_XML_FILES</span><span class="ch">)</span></span>
<span id="cb9-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> indexcache</span></span></code></pre></div><p>This rule will generate for every file in <code class="verbatim">site/posts/*.html</code> a corresponding <code class="verbatim">xml</code> file (<code class="verbatim">hxclean</code>
takes an HTML an try its best to make an XML out of it).</p><h2 id="html-index">HTML Index</h2><p>Now we just want to generate the main <code class="verbatim">index.html</code> page at the root of the site. This
page should list all articles by date in reverse order.</p><p>The first step is to take advantage of the cache index. For every XML
file I generated before I should generate the small HTML block I want
for every entry. For this I use a script <code class="verbatim">mk-index-entry.sh</code>. He will use <code class="verbatim">hxselect</code> to retrieve the date and the title from
the cached XML files. Then generate a small file just containing the
date and the link.</p><p>Here is the block in the Makefile:</p><div class="sourceCode" id="cb10"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb10-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_INDEX_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> %.xml</span><span class="kw">,</span><span class="st">%.index</span><span class="kw">,</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_XML_FILES</span><span class="ch">))</span></span>
<span id="cb10-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="dt">MK_INDEX_ENTRY </span><span class="ch">:=</span><span class="st"> ./engine/mk-index-entry.sh</span></span>
<span id="cb10-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="dt">INDEX_CACHE_DIR </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">CACHE_DIR</span><span class="ch">)</span><span class="st">/rss</span></span>
<span id="cb10-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="dv">$(INDEX_CACHE_DIR)/%.index:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">INDEX_CACHE_DIR</span><span class="ch">)</span><span class="dt">/%.xml </span><span class="ch">$(</span><span class="dt">MK_INDEX_ENTRY</span><span class="ch">)</span></span>
<span id="cb10-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb10-5" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="dt">INDEX_CACHE_DIR</span><span class="ch">)</span></span>
<span id="cb10-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb10-6" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">MK_INDEX_ENTRY</span><span class="ch">)</span> <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span></code></pre></div><p>It means: for every <code class="verbatim">.xml</code> file generate
a <code class="verbatim">.index</code> file with <code class="verbatim">mk-index-entry.sh</code>.</p><div class="sourceCode" id="cb11" data-org-language="sh"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb11-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env zsh</span></span>
<span id="cb11-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="co"># prelude</span></span>
<span id="cb11-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">git</span> rev-parse <span class="at">--show-toplevel</span><span class="va">)</span><span class="st">&quot;</span> <span class="kw">||</span> <span class="bu">exit</span> 1</span>
<span id="cb11-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="va">xfic</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$1</span><span class="st">&quot;</span></span>
<span id="cb11-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="va">dst</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$2</span><span class="st">&quot;</span></span>
<span id="cb11-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-7" aria-hidden="true" tabindex="-1"></a><span class="va">indexdir</span><span class="op">=</span><span class="st">&quot;.cache/rss&quot;</span></span>
<span id="cb11-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-9" aria-hidden="true" tabindex="-1"></a><span class="co"># HTML Accessors (similar to CSS accessors)</span></span>
<span id="cb11-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-10" aria-hidden="true" tabindex="-1"></a><span class="va">dateaccessor</span><span class="op">=</span><span class="st">&#39;.yyydate&#39;</span></span>
<span id="cb11-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-11" aria-hidden="true" tabindex="-1"></a><span class="co"># title and keyword shouldn&#39;t be changed</span></span>
<span id="cb11-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-12" aria-hidden="true" tabindex="-1"></a><span class="va">titleaccessor</span><span class="op">=</span><span class="st">&#39;title&#39;</span></span>
<span id="cb11-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-13" aria-hidden="true" tabindex="-1"></a><span class="fu">finddate()</span><span class="kw">{</span> <span class="op">&lt;</span> <span class="va">$1</span> <span class="ex">hxselect</span> <span class="at">-c</span> <span class="va">$dateaccessor</span> <span class="kw">|</span> <span class="fu">sed</span> <span class="st">&#39;s/\[//g;s/\]//g;s/ .*$//&#39;</span> }</span>
<span id="cb11-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-14" aria-hidden="true" tabindex="-1"></a><span class="fu">findtitle()</span><span class="kw">{</span> <span class="op">&lt;</span> <span class="va">$1</span> <span class="ex">hxselect</span> <span class="at">-c</span> <span class="va">$titleaccessor</span> }</span>
<span id="cb11-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-16"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-16" aria-hidden="true" tabindex="-1"></a><span class="ex">autoload</span> <span class="at">-U</span> colors <span class="kw">&amp;&amp;</span> <span class="ex">colors</span></span>
<span id="cb11-17"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-18"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-18" aria-hidden="true" tabindex="-1"></a><span class="va">blogfile</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="bu">echo</span> <span class="st">&quot;</span><span class="va">$xfic</span><span class="st">&quot;</span><span class="kw">|</span><span class="fu">sed</span> <span class="st">&#39;s#.xml$#.html#;s#^&#39;</span><span class="va">$indexdir</span><span class="st">&#39;/#posts/#&#39;</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb11-19"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-19" aria-hidden="true" tabindex="-1"></a><span class="bu">printf</span> <span class="st">&quot;%-30s&quot;</span> <span class="va">$blogfile</span></span>
<span id="cb11-20"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-20" aria-hidden="true" tabindex="-1"></a><span class="va">d</span><span class="op">=</span><span class="va">$(</span><span class="ex">finddate</span> <span class="va">$xfic)</span></span>
<span id="cb11-21"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-21" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="at">-n</span> <span class="st">&quot; [</span><span class="va">$d</span><span class="st">]&quot;</span></span>
<span id="cb11-22"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-22" aria-hidden="true" tabindex="-1"></a><span class="va">rssdate</span><span class="op">=</span><span class="va">$(</span><span class="ex">formatdate</span> <span class="va">$d)</span></span>
<span id="cb11-23"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-23" aria-hidden="true" tabindex="-1"></a><span class="va">title</span><span class="op">=</span><span class="va">$(</span><span class="ex">findtitle</span> <span class="va">$xfic)</span></span>
<span id="cb11-24"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-24" aria-hidden="true" tabindex="-1"></a><span class="va">keywords</span><span class="op">=</span><span class="va">(</span> <span class="va">$(</span><span class="ex">findkeywords</span> <span class="va">$xfic)</span> <span class="va">)</span></span>
<span id="cb11-25"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-25" aria-hidden="true" tabindex="-1"></a><span class="bu">printf</span> <span class="st">&quot;: %-55s&quot;</span> <span class="st">&quot;</span><span class="va">$title</span><span class="st"> (</span><span class="va">$keywords</span><span class="st">)&quot;</span></span>
<span id="cb11-26"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-26" aria-hidden="true" tabindex="-1"></a><span class="kw">{</span> <span class="bu">printf</span> <span class="st">&quot;</span><span class="dt">\\</span><span class="st">n&lt;li&gt;&quot;</span></span>
<span id="cb11-27"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-27" aria-hidden="true" tabindex="-1"></a>  <span class="bu">printf</span> <span class="st">&quot;</span><span class="dt">\\</span><span class="st">n&lt;span class=</span><span class="dt">\&quot;</span><span class="st">pubDate</span><span class="dt">\&quot;</span><span class="st">&gt;%s&lt;/span&gt;&quot;</span> <span class="st">&quot;</span><span class="va">$d</span><span class="st">&quot;</span></span>
<span id="cb11-28"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-28" aria-hidden="true" tabindex="-1"></a>  <span class="bu">printf</span> <span class="st">&quot;</span><span class="dt">\\</span><span class="st">n&lt;a href=</span><span class="dt">\&quot;</span><span class="st">%s</span><span class="dt">\&quot;</span><span class="st">&gt;%s&lt;/a&gt;&quot;</span> <span class="st">&quot;</span><span class="va">${blogfile}</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$title</span><span class="st">&quot;</span></span>
<span id="cb11-29"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-29" aria-hidden="true" tabindex="-1"></a>  <span class="bu">printf</span> <span class="st">&quot;</span><span class="dt">\\</span><span class="st">n&lt;/li&gt;</span><span class="dt">\\</span><span class="st">n</span><span class="dt">\\</span><span class="st">n&quot;</span></span>
<span id="cb11-30"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-30" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span> <span class="op">&gt;&gt;</span> <span class="va">${dst}</span></span>
<span id="cb11-31"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-32"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb11-32" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot; [</span><span class="va">${fg</span><span class="op">[</span>green<span class="op">]</span><span class="va">}</span><span class="st">OK</span><span class="va">${reset_color}</span><span class="st">]&quot;</span></span></code></pre></div><p>Then I use these intermediate files to generate a single bigger index
file.</p><div class="sourceCode" id="cb12"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb12-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="dt">HTML_INDEX </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/index.html</span></span>
<span id="cb12-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="dt">MKINDEX </span><span class="ch">:=</span><span class="st"> engine/mk-index.sh</span></span>
<span id="cb12-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="dt">INDEX_TEMPLATE </span><span class="ch">?=</span><span class="st"> templates/index.html</span></span>
<span id="cb12-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-4" aria-hidden="true" tabindex="-1"></a><span class="dv">$(HTML_INDEX):</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_INDEX_FILES</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">MKINDEX</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">INDEX_TEMPLATE</span><span class="ch">)</span></span>
<span id="cb12-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-5" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span></span>
<span id="cb12-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-6" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">MKINDEX</span><span class="ch">)</span></span>
<span id="cb12-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> index</span></span>
<span id="cb12-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-8" aria-hidden="true" tabindex="-1"></a><span class="dv">index:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">HTML_INDEX</span><span class="ch">)</span></span>
<span id="cb12-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb12-9" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> index</span></span></code></pre></div><p>This script is a big one, but it is not that complex. For every file,
I generate a new file <code class="verbatim">DATE-dirname</code>. I sort
them in reverse order and put their content in the middle of an HTML
file.</p><p>Important note: this file updates only if the index change.</p><p>The first part of the script creates files with the creation date in
their metadatas. The created file name will contain the creation date,
this will be helpful later.</p><div class="sourceCode" id="cb13" data-org-language="sh"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env zsh</span></span>
<span id="cb13-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="ex">autoload</span> <span class="at">-U</span> colors <span class="kw">&amp;&amp;</span> <span class="ex">colors</span></span>
<span id="cb13-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$(</span><span class="fu">git</span> rev-parse <span class="at">--show-toplevel</span><span class="va">)</span><span class="st">&quot;</span> <span class="kw">||</span> <span class="bu">exit</span> 1</span>
<span id="cb13-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Directory</span></span>
<span id="cb13-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-6" aria-hidden="true" tabindex="-1"></a><span class="va">webdir</span><span class="op">=</span><span class="st">&quot;_site&quot;</span></span>
<span id="cb13-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-7" aria-hidden="true" tabindex="-1"></a><span class="va">indexfile</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$webdir</span><span class="st">/index.html&quot;</span></span>
<span id="cb13-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="va">indexdir</span><span class="op">=</span><span class="st">&quot;.cache/rss&quot;</span></span>
<span id="cb13-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-9" aria-hidden="true" tabindex="-1"></a><span class="va">tmpdir</span><span class="op">=</span><span class="va">$(</span><span class="fu">mktemp</span> <span class="at">-d</span><span class="va">)</span></span>
<span id="cb13-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-11" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;Publishing&quot;</span></span>
<span id="cb13-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-13" aria-hidden="true" tabindex="-1"></a><span class="va">dateaccessor</span><span class="op">=</span><span class="st">&#39;.pubDate&#39;</span></span>
<span id="cb13-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-14" aria-hidden="true" tabindex="-1"></a><span class="fu">finddate()</span><span class="kw">{</span> <span class="op">&lt;</span> <span class="va">$1</span> <span class="ex">hxselect</span> <span class="at">-c</span> <span class="va">$dateaccessor</span> }</span>
<span id="cb13-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-15" aria-hidden="true" tabindex="-1"></a><span class="co"># generate files with &lt;DATE&gt;-&lt;FILENAME&gt;.index</span></span>
<span id="cb13-16"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-16" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> fic <span class="kw">in</span> <span class="va">$indexdir</span>/<span class="pp">**</span>/<span class="pp">*</span>.index<span class="kw">;</span> <span class="cf">do</span></span>
<span id="cb13-17"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-17" aria-hidden="true" tabindex="-1"></a>    <span class="va">d</span><span class="op">=</span><span class="va">$(</span><span class="ex">finddate</span> <span class="va">$fic)</span></span>
<span id="cb13-18"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-18" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">${$</span><span class="er">{fic:h</span><span class="va">}</span><span class="st">:t} [</span><span class="va">$d</span><span class="st">]&quot;</span></span>
<span id="cb13-19"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-19" aria-hidden="true" tabindex="-1"></a>    <span class="fu">cp</span> <span class="va">$fic</span> <span class="va">$tmpdir</span>/<span class="va">$d</span>-<span class="va">${$</span><span class="er">{fic:h</span><span class="va">}</span>:t}.index</span>
<span id="cb13-20"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb13-20" aria-hidden="true" tabindex="-1"></a><span class="cf">done</span></span></code></pre></div><p>Then I use these files to generate a file that will contain the <code class="verbatim">body</code> of the HTML.</p><div class="sourceCode" id="cb14" data-org-language="sh"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb14-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="co"># for every post in reverse order</span></span>
<span id="cb14-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="co"># generate the body (there is some logic to group by year)</span></span>
<span id="cb14-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="va">previousyear</span><span class="op">=</span><span class="st">&quot;&quot;</span></span>
<span id="cb14-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> fic <span class="kw">in</span> <span class="va">$(</span><span class="fu">ls</span> <span class="va">$tmpdir</span>/<span class="pp">*</span>.index <span class="kw">|</span> <span class="fu">sort</span> <span class="at">-r</span><span class="va">)</span><span class="kw">;</span> <span class="cf">do</span></span>
<span id="cb14-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-5" aria-hidden="true" tabindex="-1"></a>    <span class="va">d</span><span class="op">=</span><span class="va">$(</span><span class="ex">finddate</span> <span class="va">$fic)</span></span>
<span id="cb14-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-6" aria-hidden="true" tabindex="-1"></a>    <span class="va">year</span><span class="op">=</span><span class="va">$(</span> <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">$d</span><span class="st">&quot;</span> <span class="kw">|</span> <span class="fu">perl</span> <span class="at">-pe</span> <span class="st">&#39;s#(\d{4})-.*#$1#&#39;</span><span class="va">)</span></span>
<span id="cb14-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">((</span> <span class="va">year</span> <span class="op">!=</span> <span class="va">previousyear</span> <span class="kw">));</span> <span class="cf">then</span></span>
<span id="cb14-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">((</span> <span class="va">previousyear</span> <span class="op">&gt;</span> <span class="dv">0</span> <span class="kw">));</span> <span class="cf">then</span></span>
<span id="cb14-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-9" aria-hidden="true" tabindex="-1"></a>            <span class="bu">echo</span> <span class="st">&quot;&lt;/ul&gt;&quot;</span> <span class="op">&gt;&gt;</span> <span class="va">$tmpdir</span>/index</span>
<span id="cb14-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">fi</span></span>
<span id="cb14-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-11" aria-hidden="true" tabindex="-1"></a>        <span class="va">previousyear</span><span class="op">=</span><span class="va">$year</span></span>
<span id="cb14-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-12" aria-hidden="true" tabindex="-1"></a>        <span class="bu">echo</span> <span class="st">&quot;&lt;h3 name=</span><span class="dt">\&quot;</span><span class="va">${year}</span><span class="dt">\&quot;</span><span class="st"> &gt;</span><span class="va">${year}</span><span class="st">&lt;/h3&gt;&lt;ul&gt;&quot;</span> <span class="op">&gt;&gt;</span> <span class="va">$tmpdir</span>/index</span>
<span id="cb14-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">fi</span></span>
<span id="cb14-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-14" aria-hidden="true" tabindex="-1"></a>    <span class="fu">cat</span> <span class="va">$fic</span> <span class="op">&gt;&gt;</span> <span class="va">$tmpdir</span>/index</span>
<span id="cb14-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-15" aria-hidden="true" tabindex="-1"></a><span class="cf">done</span></span>
<span id="cb14-16"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb14-16" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;&lt;/ul&gt;&quot;</span> <span class="op">&gt;&gt;</span> <span class="va">$tmpdir</span>/index</span></code></pre></div><p>And finally, I render the HTML using a template within a shell
script:</p><div class="sourceCode" id="cb15" data-org-language="sh"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb15-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="va">title</span><span class="op">=</span><span class="st">&quot;Y&quot;</span></span>
<span id="cb15-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="va">description</span><span class="op">=</span><span class="st">&quot;Most recent articles&quot;</span></span>
<span id="cb15-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="va">author</span><span class="op">=</span><span class="st">&quot;Yann Esposito&quot;</span></span>
<span id="cb15-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-4" aria-hidden="true" tabindex="-1"></a><span class="va">body</span><span class="op">=</span><span class="va">$(</span><span class="op">&lt;</span> <span class="va">$tmpdir</span>/index<span class="va">)</span></span>
<span id="cb15-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-5" aria-hidden="true" tabindex="-1"></a><span class="va">date</span><span class="op">=</span><span class="va">$(LC_TIME</span><span class="op">=</span>en_US <span class="fu">date</span> +<span class="st">&#39;%Y-%m-%d&#39;</span><span class="va">)</span></span>
<span id="cb15-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-7" aria-hidden="true" tabindex="-1"></a><span class="co"># A neat trick to use pandoc template within a shell script</span></span>
<span id="cb15-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-8" aria-hidden="true" tabindex="-1"></a><span class="co"># the pandoc templates use $x$ format, we replace it by just $x</span></span>
<span id="cb15-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-9" aria-hidden="true" tabindex="-1"></a><span class="co"># to be used with envsubst</span></span>
<span id="cb15-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-10" aria-hidden="true" tabindex="-1"></a><span class="va">template</span><span class="op">=</span><span class="va">$(</span><span class="op">&lt;</span> templates/index.html <span class="kw">|</span> <span class="dt">\</span></span>
<span id="cb15-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-11" aria-hidden="true" tabindex="-1"></a>    <span class="fu">sed</span> <span class="st">&#39;s/\$\(header-includes\|table-of-content\)\$//&#39;</span> <span class="kw">|</span> <span class="dt">\</span></span>
<span id="cb15-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-12" aria-hidden="true" tabindex="-1"></a>    <span class="fu">sed</span> <span class="st">&#39;s/\$if.*\$//&#39;</span> <span class="kw">|</span> <span class="dt">\</span></span>
<span id="cb15-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-13" aria-hidden="true" tabindex="-1"></a>    <span class="fu">perl</span> <span class="at">-pe</span> <span class="st">&#39;s#(\$[^\$]*)\$#$1#g&#39;</span> <span class="va">)</span></span>
<span id="cb15-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-14" aria-hidden="true" tabindex="-1"></a><span class="kw">{</span></span>
<span id="cb15-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-15" aria-hidden="true" tabindex="-1"></a>    <span class="bu">export</span> <span class="va">title</span></span>
<span id="cb15-16"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-16" aria-hidden="true" tabindex="-1"></a>    <span class="bu">export</span> <span class="va">author</span></span>
<span id="cb15-17"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-17" aria-hidden="true" tabindex="-1"></a>    <span class="bu">export</span> <span class="va">description</span></span>
<span id="cb15-18"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-18" aria-hidden="true" tabindex="-1"></a>    <span class="bu">export</span> <span class="va">date</span></span>
<span id="cb15-19"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-19" aria-hidden="true" tabindex="-1"></a>    <span class="bu">export</span> <span class="va">body</span></span>
<span id="cb15-20"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-20" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="va">${template}</span> <span class="kw">|</span> <span class="ex">envsubst</span></span>
<span id="cb15-21"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-21" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span> <span class="op">&gt;</span> <span class="st">&quot;</span><span class="va">$indexfile</span><span class="st">&quot;</span></span>
<span id="cb15-22"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb15-23"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-23" aria-hidden="true" tabindex="-1"></a><span class="fu">rm</span> <span class="at">-rf</span> <span class="va">$tmpdir</span></span>
<span id="cb15-24"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb15-24" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;* HTML INDEX [done]&quot;</span></span></code></pre></div><h2 id="rss">RSS</h2><p>My RSS generation is similar to the system I used to generate the
index file. I just slightly improved the rules.</p><p>The <code class="verbatim">Makefile</code> blocks look like:</p><div class="sourceCode" id="cb16"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb16-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co"># RSS</span></span>
<span id="cb16-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_RSS_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> %.xml</span><span class="kw">,</span><span class="st">%.rss</span><span class="kw">,</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_XML_FILES</span><span class="ch">))</span></span>
<span id="cb16-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="dt">MK_RSS_ENTRY </span><span class="ch">:=</span><span class="st"> ./engine/mk-rss-entry.sh</span></span>
<span id="cb16-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-4" aria-hidden="true" tabindex="-1"></a><span class="dv">$(RSS_CACHE_DIR)/%.rss:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">RSS_CACHE_DIR</span><span class="ch">)</span><span class="dt">/%.xml </span><span class="ch">$(</span><span class="dt">MK_RSS_ENTRY</span><span class="ch">)</span></span>
<span id="cb16-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-5" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="dt">RSS_CACHE_DIR</span><span class="ch">)</span></span>
<span id="cb16-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-6" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">MK_RSS_ENTRY</span><span class="ch">)</span> <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb16-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-8" aria-hidden="true" tabindex="-1"></a><span class="dt">RSS </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/rss.xml</span></span>
<span id="cb16-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-9" aria-hidden="true" tabindex="-1"></a><span class="dt">MKRSS </span><span class="ch">:=</span><span class="st"> engine/mkrss.sh</span></span>
<span id="cb16-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-10" aria-hidden="true" tabindex="-1"></a><span class="dv">$(RSS):</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_RSS_FILES</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">MKRSS</span><span class="ch">)</span></span>
<span id="cb16-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-11" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">MKRSS</span><span class="ch">)</span></span>
<span id="cb16-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-13" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> rss</span></span>
<span id="cb16-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-14" aria-hidden="true" tabindex="-1"></a><span class="dv">rss:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">RSS</span><span class="ch">)</span></span>
<span id="cb16-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb16-15" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> rss</span></span></code></pre></div><h2 id="gemini">Gemini</h2><p>I wrote a minimal script to transform my org files to gemini files. I
also need to generate an index and an atom file for gemini:</p><div class="sourceCode" id="cb17"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb17-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="co"># ORG -&gt; GEMINI</span></span>
<span id="cb17-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="dt">EXT </span><span class="ch">:=</span><span class="st"> .org</span></span>
<span id="cb17-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-3" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_GMI_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> find </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st"> -type f -name &quot;*</span><span class="ch">$(</span><span class="dt">EXT</span><span class="ch">)</span><span class="st">&quot; </span><span class="ch">$(</span><span class="dt">NO_DRAFT</span><span class="ch">))</span></span>
<span id="cb17-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-4" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_GMI_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">subst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">EXT</span><span class="ch">)</span><span class="kw">,</span><span class="st">.gmi</span><span class="kw">,</span><span class="st"> \</span></span>
<span id="cb17-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-5" aria-hidden="true" tabindex="-1"></a><span class="st">                        </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="st"> \</span></span>
<span id="cb17-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-6" aria-hidden="true" tabindex="-1"></a><span class="st">                            </span><span class="ch">$(</span><span class="dt">SRC_GMI_FILES</span><span class="ch">)))</span></span>
<span id="cb17-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-7" aria-hidden="true" tabindex="-1"></a><span class="dt">GMI </span><span class="ch">:=</span><span class="st"> engine/org2gemini.sh</span></span>
<span id="cb17-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-8" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/%.gmi:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%.org </span><span class="ch">$(</span><span class="dt">GMI</span><span class="ch">)</span><span class="dt"> engine/org2gemini_step1.sh</span></span>
<span id="cb17-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-9" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span></span>
<span id="cb17-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-10" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">GMI</span><span class="ch">)</span> <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb17-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-11" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_GMI_FILES</span><span class="ch">)</span></span>
<span id="cb17-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-12" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> gmi</span></span>
<span id="cb17-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-13" aria-hidden="true" tabindex="-1"></a><span class="dv">gmi:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_GMI_FILES</span><span class="ch">)</span></span>
<span id="cb17-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-15" aria-hidden="true" tabindex="-1"></a><span class="co"># GEMINI INDEX</span></span>
<span id="cb17-16"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-16" aria-hidden="true" tabindex="-1"></a><span class="dt">GMI_INDEX </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/index.gmi</span></span>
<span id="cb17-17"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-17" aria-hidden="true" tabindex="-1"></a><span class="dt">MK_GMI_INDEX </span><span class="ch">:=</span><span class="st"> engine/mk-gemini-index.sh</span></span>
<span id="cb17-18"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-18" aria-hidden="true" tabindex="-1"></a><span class="dv">$(GMI_INDEX):</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_GMI_FILES</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">MK_GMI_INDEX</span><span class="ch">)</span></span>
<span id="cb17-19"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-19" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span></span>
<span id="cb17-20"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-20" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">MK_GMI_INDEX</span><span class="ch">)</span></span>
<span id="cb17-21"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-21" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">GMI_INDEX</span><span class="ch">)</span></span>
<span id="cb17-22"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-22" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> gmi-index</span></span>
<span id="cb17-23"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-23" aria-hidden="true" tabindex="-1"></a><span class="dv">gmi-index:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">GMI_INDEX</span><span class="ch">)</span></span>
<span id="cb17-24"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-25"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-25" aria-hidden="true" tabindex="-1"></a><span class="co"># RSS</span></span>
<span id="cb17-26"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-26" aria-hidden="true" tabindex="-1"></a><span class="dt">GEM_ATOM </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/gem-atom.xml</span></span>
<span id="cb17-27"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-27" aria-hidden="true" tabindex="-1"></a><span class="dt">MK_GEMINI_ATOM </span><span class="ch">:=</span><span class="st"> engine/mk-gemini-atom.sh</span></span>
<span id="cb17-28"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-28" aria-hidden="true" tabindex="-1"></a><span class="dv">$(GEM_ATOM):</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_GMI_FILES</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">MK_GEMINI_ATOM</span><span class="ch">)</span></span>
<span id="cb17-29"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-29" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">MK_GEMINI_ATOM</span><span class="ch">)</span></span>
<span id="cb17-30"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-30" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">GEM_ATOM</span><span class="ch">)</span></span>
<span id="cb17-31"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-31" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> gmi-atom</span></span>
<span id="cb17-32"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-32" aria-hidden="true" tabindex="-1"></a><span class="dv">gmi-atom:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">GMI_ATOM</span><span class="ch">)</span></span>
<span id="cb17-33"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-34"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-34" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> gemini</span></span>
<span id="cb17-35"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb17-35" aria-hidden="true" tabindex="-1"></a><span class="dv">gemini:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_GMI_FILES</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">GMI_INDEX</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">GEM_ATOM</span><span class="ch">)</span></span></code></pre></div><h2 id="images">Images</h2><p>For images, I try to compress them all with imagemagick.</p><div class="sourceCode" id="cb18"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb18-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Images</span></span>
<span id="cb18-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_IMG_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> find </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st"> -type f -name &quot;*.jpg&quot; -or -name &quot;*.jpeg&quot; -or -name &quot;*.gif&quot; -or -name &quot;*.png&quot;</span><span class="ch">)</span></span>
<span id="cb18-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_IMG_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_IMG_FILES</span><span class="ch">))</span></span>
<span id="cb18-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/%.jpg:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%.jpg</span></span>
<span id="cb18-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-6" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span></span>
<span id="cb18-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-7" aria-hidden="true" tabindex="-1"></a>    convert <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> -quality 50 -resize 800x800\&gt; <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb18-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-9" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/%.jpg:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%.jpeg</span></span>
<span id="cb18-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-10" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span></span>
<span id="cb18-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-11" aria-hidden="true" tabindex="-1"></a>    convert <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> -quality 50 -resize 800x800\&gt; <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb18-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-13"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-13" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/%.gif:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%.gif</span></span>
<span id="cb18-14"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-14" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span></span>
<span id="cb18-15"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-15" aria-hidden="true" tabindex="-1"></a>    convert <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> -quality 50 -resize 800x800\&gt; <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb18-16"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-17"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-17" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/%.png:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%.png</span></span>
<span id="cb18-18"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-18" aria-hidden="true" tabindex="-1"></a>    <span class="ch">@</span><span class="fu">mkdir -p </span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span></span>
<span id="cb18-19"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-19" aria-hidden="true" tabindex="-1"></a>    convert <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> -quality 50 -resize 800x800\&gt; <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span>
<span id="cb18-20"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-21"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-21" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> img</span></span>
<span id="cb18-22"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-22" aria-hidden="true" tabindex="-1"></a><span class="dv">img:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_IMG_FILES</span><span class="ch">)</span></span>
<span id="cb18-23"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb18-23" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_IMG_FILES</span><span class="ch">)</span></span></code></pre></div><h2 id="deploy">Deploy</h2><p>A nice bonus is that I also deploy my website using make.</p><div class="sourceCode" id="cb19"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb19-1"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="co"># DEPLOY</span></span>
<span id="cb19-2"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> site</span></span>
<span id="cb19-3"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="dv">site:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">ALL</span><span class="ch">)</span></span>
<span id="cb19-4"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-5"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> deploy</span></span>
<span id="cb19-6"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="dv">deploy:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">ALL</span><span class="ch">)</span></span>
<span id="cb19-7"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-7" aria-hidden="true" tabindex="-1"></a>    engine/sync.sh</span>
<span id="cb19-8"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-9"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-9" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> clean</span></span>
<span id="cb19-10"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-10" aria-hidden="true" tabindex="-1"></a><span class="dv">clean:</span></span>
<span id="cb19-11"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-11" aria-hidden="true" tabindex="-1"></a>    <span class="ch">-</span><span class="fu">[ ! -z </span><span class="st">&quot;</span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">&quot;</span><span class="fu"> ] &amp;&amp; rm -rf </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="fu">/*</span></span>
<span id="cb19-12"><a href="https://yannesposito.com/posts/0018-makefile-as-static-site-builder-follow-up/index.html#cb19-12" aria-hidden="true" tabindex="-1"></a>    <span class="ch">-</span><span class="fu">[ ! -z </span><span class="st">&quot;</span><span class="ch">$(</span><span class="dt">CACHE_DIR</span><span class="ch">)</span><span class="st">&quot;</span><span class="fu"> ] &amp;&amp; rm -rf </span><span class="ch">$(</span><span class="dt">CACHE_DIR</span><span class="ch">)</span><span class="fu">/*</span></span></code></pre></div></article></main>
]]></description>
</item>


<item>
<title>Static Blog Builder</title>
<guid>https://yannesposito.com/posts/0017-static-blog-builder/index.html</guid>
<pubDate>Sat, 01 May 2021 00:00:00 +0200</pubDate>
<category>blog</category>
<category>static</category>
<description><![CDATA[
<main id="content"><article><p>As someone on the Internet said not so far ago. Building its own
static building system is a rite of passage for many developers. It has
a lot of nice features. It gives a goal with a feeling of
accomplishment. It is simple enough so most developers could build their
own system. It could also become complex when you go down the rabbit
hole.</p><p>Along the years I used different tools and used and wrote of few
static website systems:</p><ul><li><a href="https://nanoc.app">nanoc</a> (in Ruby), at that time it
looked like this: <a href="https://web.archive.org/web/20081002071448/http://nanoc.stoneship.org/">old
nanoc 2 website</a></li><li><a href="https://jaspervdj.be/hakyll/">hakyll</a> (haskell static
website generator)</li><li><a href="https://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html">org-publish</a>
(emacs package in conjunction with org-mode)</li><li><a href="https://shakebuild.com">shake</a> (haskell again)</li></ul><p>So if you look at the progression, I first used nanoc because I used
ruby and it was a new solution, the website looked really great. Also
the main developer <a href="https://denisdefreyne.com">Denis
Defreyne</a> was really helpful. Ruby was really great at dealing with
regular expressions for hacking my documents.</p><p>Then I was interested in Haskell, and I switched to a Haskell-made
solution. I used hakyll, and I wrote a bit about it in <a href="http://yannesposito.com/Scratch/en/blog/Hakyll-setup/">Hakyll
Setup</a>. As a side note, the author of Hakyll <a href="https://jaspervdj.be/hakyll/">Jasper Van der Jeugt</a> is
apparently a friend of the author of nanoc. They both wrote a static
site generators with their preferred programming language. I added a lot
of personal features to my own site builder. It was a nice toy
project.</p><p>Then, due to a major disruption in my professional and private life I
stopped to take care of my website.</p><p>And a few years ago, I wanted to start a new website from scratch. In
the meantime I switched my editor of choice from vim to Emacs. I started
to work in Clojure and emacs is generally a natural choice because you
can configure it with LISP. I discovered <a href="https://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html">org-mode</a>
(I don't think the homepage of org mode makes justice to how incredible
it is). So org-mode comes with an export system. Thus I switched to
org-publish. Again <a href="https://her.esy.fun/posts/0001-new-blog/index.html">I wrote a bit
about it</a>.</p><p>It was nice but slow. I improved a few things like writing a short
script to <a href="https://her.esy.fun/posts/0005-rss-gen/index.html">Generate RSS
from a tree of html files.</a> I still had the feeling it was too
slow.</p><p>Static site building is a specific usage of a build system. And as I
knew I could use <code class="verbatim">pandoc</code> to build HTML out
of org-mode files and still versed in the Haskell culture I decided to
try <a href="https://shakebuild.com">shake</a>. You can learn more by
reading this excellent paper about it, I think all developer should read
it: <a href="https://github.com/snowleopard/build-systems/releases/download/icfp-submission/build-systems.pdf">Build
System à la carte</a>.</p><p>As a bonus, <a href="https://pandoc.org">pandoc</a> is written in
Haskell. I could then directly use the <a href="https://pandoc.org">pandoc</a> library in my build program. It
worked like a charm and it was <strong>very fast</strong> as compared to
other solutions I tried. So really let me tell you shake is a great
build system.</p><p>Unfortunately it was not perfect. While it was very fast, and I was
able to use pandoc API directly. It made me dependent on Haskell. The
best way I found to have Haskell reproducible build environment is to
use <a href="https://nixos.org/nix">nix</a>. This was great until the
Big Sur update. To keep it short, nix stopped working on my computers
after I upgraded my to Big Sur. Gosh, it was painful to fix.</p><p>Concurrently I discovered <a href="file:///posts/0016-gemini/index.html">gemini</a> and wanted to
duplicate my website into gemini sphere. So I tried to update my build
system but my code was to oriented to use pandoc and it was painful to
have gemini in the middle of it. Particularly, generating a gemini index
file. My main goal was to have gemini file that could only be linked
from withing gemini sphere. Because gemini is a lot smaller web where
you could feel a bit more protected from what the Web has become along
the years. Whatever, in the end, I just had two problems to tackles.</p><ol><li>Haskell became difficult to trust as very stable tool. Stable in the
sense that I would not have any support work to do in order to keep just
using it and not fixing/tweaking it.</li><li>Simplify the overall system to have a simpler build description</li></ol><p>So a very stable tool that I am pretty sure will still work almost
exactly as today in 10 years is <strong><code class="verbatim">make</code></strong> (more precisely gnumake). I
expected a lot of people had already come to the same conclusion and
wrote about it. To my great surprise, I found very few article about
generating static website with make. I only found solutions a bit too
specific for my need. This is why I would like to give you a more
generic starting point solution.</p><h1 id="the--makefile-">The <code class="verbatim">Makefile</code></h1><p>Instead of copy/pasting my current <code class="verbatim">Makefile</code> entirely let me give you a more generic
one. It should be a great start.</p><p>The first part will be used to simply copy the files from <code class="verbatim">src/</code> to <code class="verbatim">_site/</code>.</p><div class="sourceCode" id="cb1"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb1-1"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dv">all:</span><span class="dt"> website</span></span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co"># directory containing my org files as well as my assets files</span></span>
<span id="cb1-4"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_DIR </span><span class="ch">?=</span><span class="st"> src</span></span>
<span id="cb1-5"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co"># directory where I will but the files for my website (HTML + assets)</span></span>
<span id="cb1-6"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_DIR </span><span class="ch">?=</span><span class="st"> _site</span></span>
<span id="cb1-7"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="co"># list all files in src</span></span>
<span id="cb1-9"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="co"># if you want to exclude .org files use the exclude from the find command</span></span>
<span id="cb1-10"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_RAW_FILES </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> find </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st"> -type f</span><span class="ch">)</span></span>
<span id="cb1-11"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="co"># generate all file that should be copied in the site</span></span>
<span id="cb1-12"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co"># For my site, I want to publish my source files along the HTML files</span></span>
<span id="cb1-13"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_RAW_FILES   </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">patsubst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/%</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">SRC_RAW_FILES</span><span class="ch">))</span></span>
<span id="cb1-14"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL             </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_RAW_FILES</span><span class="ch">)</span></span>
<span id="cb1-15"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-16"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="co"># COPY EVERYTHING (.org file included)</span></span>
<span id="cb1-17"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/% :</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%</span></span>
<span id="cb1-18"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-18" aria-hidden="true" tabindex="-1"></a>    mkdir -p <span class="st">&quot;</span><span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span><span class="st">&quot;</span></span>
<span id="cb1-19"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb1-19" aria-hidden="true" tabindex="-1"></a>    cp <span class="st">&quot;</span><span class="ch">$&lt;</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="ch">$@</span><span class="st">&quot;</span></span></code></pre></div><p>This part is about running the <code class="verbatim">pandoc</code>
command for all <code class="verbatim">org</code> files in <code class="verbatim">src/</code> so they generate a html file in <code class="verbatim">_site/</code>.</p><div class="sourceCode" id="cb2"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb2-1"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># ORG -&gt; HTML, If you prefer markdown replace .org by .md</span></span>
<span id="cb2-2"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="dt">EXT </span><span class="ch">:=</span><span class="st"> .org</span></span>
<span id="cb2-3"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="co"># all source file we&#39;ll pass to pandoc</span></span>
<span id="cb2-4"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="dt">SRC_PANDOC_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">shell</span><span class="st"> find </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="st"> -type f -name &quot;*</span><span class="ch">$(</span><span class="dt">EXT</span><span class="ch">)</span><span class="st">&quot;</span><span class="ch">)</span></span>
<span id="cb2-5"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co"># all destination files we expect (replace the extension by .html)</span></span>
<span id="cb2-6"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="dt">DST_PANDOC_FILES </span><span class="ch">?=</span><span class="st"> </span><span class="ch">$(</span><span class="kw">subst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">EXT</span><span class="ch">)</span><span class="kw">,</span><span class="st">.html</span><span class="kw">,</span><span class="st"> \</span></span>
<span id="cb2-7"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="st">                        </span><span class="ch">$(</span><span class="kw">subst</span><span class="st"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="kw">,</span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="kw">,</span><span class="st"> \</span></span>
<span id="cb2-8"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="st">                            </span><span class="ch">$(</span><span class="dt">SRC_PANDOC_FILES</span><span class="ch">)))</span></span>
<span id="cb2-9"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL              </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_PANDOC_FILES</span><span class="ch">)</span></span>
<span id="cb2-10"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-11"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="co"># use a template (you should use one)</span></span>
<span id="cb2-12"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="dt">TEMPLATE </span><span class="ch">?=</span><span class="st"> templates/post.html</span></span>
<span id="cb2-13"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="co"># URL of the CSS put yours</span></span>
<span id="cb2-14"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="dt">CSS </span><span class="ch">=</span><span class="st"> /css/y.css</span></span>
<span id="cb2-15"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="co"># The pandoc command to run to generate an html out of a source file</span></span>
<span id="cb2-16"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="dt">PANDOC </span><span class="ch">:=</span><span class="st"> pandoc </span><span class="ch">\</span></span>
<span id="cb2-17"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="st">            -c </span><span class="ch">$(</span><span class="dt">CSS</span><span class="ch">)</span><span class="st"> </span><span class="ch">\</span></span>
<span id="cb2-18"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="st">            --template=</span><span class="ch">$(</span><span class="dt">TEMPLATE</span><span class="ch">)</span><span class="st"> </span><span class="ch">\</span></span>
<span id="cb2-19"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="st">            --from org </span><span class="ch">\</span></span>
<span id="cb2-20"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="st">            --to html5 </span><span class="ch">\</span></span>
<span id="cb2-21"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-21" aria-hidden="true" tabindex="-1"></a><span class="st">            --standalone</span></span>
<span id="cb2-22"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-23"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-23" aria-hidden="true" tabindex="-1"></a><span class="co"># Generate all html if the org file change or the template change</span></span>
<span id="cb2-24"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-24" aria-hidden="true" tabindex="-1"></a><span class="dv">$(DST_DIR)/%.html:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">SRC_DIR</span><span class="ch">)</span><span class="dt">/%.org </span><span class="ch">$(</span><span class="dt">TEMPLATE</span><span class="ch">)</span></span>
<span id="cb2-25"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-25" aria-hidden="true" tabindex="-1"></a>    mkdir -p <span class="ch">$(</span><span class="kw">dir</span><span class="st"> </span><span class="ch">$@)</span></span>
<span id="cb2-26"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-26" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">PANDOC</span><span class="ch">)</span> <span class="ch">$&lt;</span> <span class="ch">\</span></span>
<span id="cb2-27"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-27" aria-hidden="true" tabindex="-1"></a>        --output <span class="ch">$@</span></span>
<span id="cb2-28"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb2-28" aria-hidden="true" tabindex="-1"></a></span></code></pre></div><p>A missing part is often the part where you would like to generate an
index page to list the latest posts. Here you are a bit alone, you need
to make one yourself. There is not generic way to do this one.</p><div class="sourceCode" id="cb3"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb3-1"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Generating an index page is not difficult but not trivial either</span></span>
<span id="cb3-2"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="dt">HTML_INDEX </span><span class="ch">:=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="st">/index.html</span></span>
<span id="cb3-3"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="dt">MKINDEX </span><span class="ch">:=</span><span class="st"> engine/mk-index.sh</span></span>
<span id="cb3-4"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="dv">$(HTML_INDEX):</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">DST_PANDOC_FILES</span><span class="ch">)</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">MKINDEX</span><span class="ch">)</span></span>
<span id="cb3-5"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb3-5" aria-hidden="true" tabindex="-1"></a>    mkdir -p <span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span></span>
<span id="cb3-6"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="ch">$(</span><span class="dt">MKINDEX</span><span class="ch">)</span></span>
<span id="cb3-7"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="dt">ALL </span><span class="ch">+=</span><span class="st"> </span><span class="ch">$(</span><span class="dt">HTML_INDEX</span><span class="ch">)</span></span></code></pre></div><p>Finally, a few useful make commands. <code class="verbatim">make clean</code> and <code class="verbatim">make deploy</code>.</p><div class="sourceCode" id="cb4"><pre class="sourceCode makefile"><code class="sourceCode makefile"><span id="cb4-1"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># make deploy will deploy the files to my website write your own script</span></span>
<span id="cb4-2"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="dv">deploy:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">ALL</span><span class="ch">)</span></span>
<span id="cb4-3"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-3" aria-hidden="true" tabindex="-1"></a>    engine/deploy.sh</span>
<span id="cb4-4"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="dv">website:</span><span class="dt"> </span><span class="ch">$(</span><span class="dt">ALL</span><span class="ch">)</span></span>
<span id="cb4-6"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="ot">.PHONY:</span><span class="dt"> clean</span></span>
<span id="cb4-8"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="dv">clean:</span></span>
<span id="cb4-10"><a href="https://yannesposito.com/posts/0017-static-blog-builder/index.html#cb4-10" aria-hidden="true" tabindex="-1"></a>    <span class="ch">-</span><span class="fu">rm -rf </span><span class="ch">$(</span><span class="dt">DST_DIR</span><span class="ch">)</span><span class="fu">/*</span></span></code></pre></div><p>Limitation: <code class="verbatim">make</code> is old. So it really
does not support spaces in filenames. Take care of that.</p><p>Let me tell you. While this is quite a minimalist approach (&lt;100
lines) it is nevertheless <strong>very fast</strong>. It will only
generate the minimal amount of work to generate your website. I have a
nice watcher script that update the website every time I save a file. It
is almost instantaneous.</p><p>The only risky dependencies for my website now is <code class="verbatim">pandoc</code>. Perhaps, they will change how they
generate an HTML from the same org file in the future. I still use <code class="verbatim">nix</code> to pin my pandoc version. The static site
builder itself is very simple, very stable and still very efficient.</p><p>As a conclusion, if you want to write your own static site builder
that's great. There are plenty of things to learn along the way. Still
if you want something stable for a long time, with a minimal amount of
dependencies, I think this Makefile is really a great start.</p></article></main>
]]></description>
</item>


<item>
<title>Gemini</title>
<guid>https://yannesposito.com/posts/0016-gemini/index.html</guid>
<pubDate>Mon, 09 Nov 2020 00:00:00 +0100</pubDate>
<category>internet</category>
<category>gopher</category>
<category>gemini</category>
<description><![CDATA[
<main id="content"><article><p>This weekend I read an article about gopher and gemini. I already
seen articles about gemini pass. Somehow, it was more appealing to me
than gopher space for totally subjective reasons I think. Anyway this
time I really dug into it, and I loved the experience.</p><p>At first sight gemini is like a parallel web for nerds. It has
fundamental changes that I would have really liked to see from the
modern web. The client decide the design, no user tracking, calm,
minimalist, simple.</p><p>Right now, on the web, most news website make the experience terrible
to read the article. The page is bloated with a lot of animations, popin
asking you to accept the cookies, ads with videos, strange fonts or
design, plenty of javascript, trackers, etc…</p><p>Gemini make those kind of anti-design impractical. In gemini space
there is no:</p><ul><li>CSS: the client decide the look of the page, the fonts, the colors,
the width of the columns, etc…</li><li>inline image: you can still link to images, but images should not be
displayed in the middle of the text.</li><li>inline links: If you link to an external resource, it is clearly
visible as an element on its own line.</li><li>cookies</li><li>HTTP headers used to track users</li><li>scripts on the client side</li><li>hard to parse format (no HTML, XHTML, etc…)</li></ul><p>Another strength of Gemini is its protocol's simplicity. It is easy
to write your own gemini server as well as your own gemini client within
a few hours of work in your programming language of choice.</p><p>And last but not least, gemini is unfit to serve "big" files. So we
should not experience people going to gemini for music/movie piracy. If
you want to serve "big" files you should use another protocol.</p><p>Personally I started to browse gemini directly from emacs with
elpher. But I know there are a lot of clients.</p><p>I liked gemini so much I hacked my blog to also publish in the gemini
sphere. I serve my pages with a rust based server. And I hacked a quick
script to transform my org mode files to gemini format. Gemini file
format is a kind of super simplified markdown. I really like the
simplicity of it.</p><p>Here are a few links to get you started.</p><p>Web:</p><ul><li><a href="https://gemini.circumlunar.space">gemini homepage</a></li><li><a href="https://thelambdalab.xyz/elpher/">elpher emacs gopher and
gemini client</a></li><li><a href="https://portal.mozz.us/gemini/gemini.circumlunar.space/">Explore
gemini from the web</a></li></ul><p>Gopher:</p><ul><li><a href="gopher://thelambdalab.xyz/1/projects/elpher/">elpher</a></li></ul><p>Gemini:</p><ul><li><a href="gemini://her.esy.fun">my website in gemini</a></li></ul></article></main>
]]></description>
</item>


<item>
<title>How I use org-mode</title>
<guid>https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html</guid>
<pubDate>Thu, 29 Oct 2020 00:00:00 +0100</pubDate>
<category>org-mode</category>
<description><![CDATA[
<main id="content"><nav id="toc" role="doc-toc"><ul><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#overview" id="toc-overview">Overview</a><ul><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#daily-routine" id="toc-daily-routine">daily
routine</a></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#document-writing" id="toc-document-writing">document
writing</a></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#note-taking" id="toc-note-taking">note taking</a></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#journaling" id="toc-journaling">journal</a></li></ul></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#task-management" id="toc-task-management">Task
Management</a><ul><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#worfklow-1--planned-tasks---org-agenda---clock" id="toc-worfklow-1--planned-tasks---org-agenda---clock">Workflow 1;
planned tasks : org-agenda + clock</a></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#workflow-2--org-capture-org-refile" id="toc-workflow-2--org-capture-org-refile">Workflow 2: Tracking;
org-capture</a></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#workflow-3--org-capture---org-refile" id="toc-workflow-3--org-capture---org-refile">Workflow 3: Add new tasks;
org-capture / org-refile</a></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#configuration" id="toc-configuration">Configuration</a></li></ul></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#conclusions" id="toc-conclusions">Conclusions</a></li><li><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#footnotes" id="toc-footnotes">Footnotes</a></li></ul></nav><article><p>In this article I would like to share a tool that was a real life
changer to me: <a href="http://orgmode.org">org mode</a>.</p><p>In my opinion emacs is worth learning just for org-mode. This is by
far the best solution I ever used to manage my tasks. I tried a lot of
differents tools before it, and this is the only one I really stick
with. It is so versatile that it can adapt to your very specific
needs.</p><p>The major difficulty faced by tasks management application is the
wrong level of complexity facing the user. This is a very hard problem
to tackle.</p><p>If your system is too simple, the users will not be able to manage
the tasks how they would like. If your system is too complex, the user
will be faced with too much details. So most successful systems have a
way to adapt their apparent complexity to the need of their users. And
org mode is exactly like that. Not only starting with org mode can be
extremely simple but also there are mostly no complexity limit.</p><p>Either org mode already handle one of your need, or most of the time
you will find a package to fulfill your need. And if not, it is easy to
write your own.</p><p>Here is the result of a few years of improving my use of org mode.
Today I can say that org mode is part of my day to day life. I still
invest a bit of time to improve minor details of my workflow time to
time. But now my daily workflow is mostly stable. So I think I can share
it.</p><h2 id="overview">Overview</h2><h3 id="daily-routine">daily routine</h3><p>The first thing I do in the morning is to open <code class="verbatim">org-agenda</code> view for today. It shows me the tasks
planned for today. What are urgent tasks, deadlines, tasks that have
deadlines in a few days, etc… I also have a glimpse of my habits, tasks
that I should start at some hour in the day, etc…</p><figure><img src="https://yannesposito.com/posts/0015-how-i-use-org-mode/img/org-super-agenda.png.webp" title="org-super-agenda" alt="Org super calendar view"></img><figcaption aria-hidden="true">Org super calendar view</figcaption></figure><p>I then start to track (<em>clock</em>) the tasks I'm currently
working on.</p><p>Often during the day, I need to create new tasks. Most of the time I
create a task and I add either a deadline or a schedule date.</p><p>Sometime I also need to deal with interruptions. In that case, I
<em>capture</em> the interruption that will also create a new task being
clocked.</p><p>At the end of the day, every tasks I worked on are saved in a <code class="verbatim">tracker.org</code> file. That file look like a date
tree. And I generally generate a <em>report</em> that tell me how much
hours I worked today. Some tasks are tagged <code class="verbatim">work</code>. The report filter only on the <code class="verbatim">work</code> tagged tasks.</p><p>Also I have some repeating tasks like review memory cards using a
spaced repetition plugin. I start it, and it shows me a few <code class="verbatim">cards</code> with questions that I review. So mainly
those cards contain info I want to keep in my mind and not only in my
notes.</p><h3 id="document-writing">document writing</h3><p>Writing documents with org mode and in particular technical document
is just incredible. Org mode feels a lot like markdown.</p><p>But org mode shine with its use of <strong>org-babel</strong>.
<strong>org-babel</strong> is used to execute code inside your document.
So you can execute block of code and get their result in block of code.
For technical writing this is extremely useful.</p><p>For example, I wrote most part of an OAuth2 provider in Clojure. And
to generate a documentation to some of our advanced users it is very
nice to provide the full HTTP request along the response.</p><p>But I also often need to play a few tricks in the doc and directly
use our Clojure code to generate JWT for example. The great part is the
ability to use those JWT generated from Clojure code in the following
code block making HTTP call.</p><p>That plus the natural ability to fold/unfold the tree structure of
the org mode file is great.</p><h3 id="note-taking">note taking</h3><p>Time to time, I need to really take the time write note on a
technical subject or sometime about articles I read about anything. For
that I use <a href="https://github.com/org-roam/org-roam">org roam</a>.
I only started to use it a few months ago. But this is a great addition
to my previous workflow that used <code class="verbatim">deft</code>
(that I still use). But I must say, this is pretty perfect as a note
taking app.</p><p>Mainly you capture notes quite easily and put links about the
subject, but also tags. In the end that generate a graph of notes that
you could use later to dig into your own notes.</p><h3 id="journaling">journal</h3><p>Along with note taking. I also try to write a journal note everyday.
For that I use org-journal (another org mode related package). I have a
default template which take care of a few metrics I want to focus on.
And I guess it is different for anyone of us.</p><h2 id="task-management">Task Management</h2><p>So here is a more detailed description about my org mode usage.</p><h3 id="worfklow-1--planned-tasks---org-agenda---clock">Workflow 1;
planned tasks : org-agenda + clock</h3><ol><li>look at the current tasks planned for today</li><li>select a task, clock it</li><li>work on the task</li><li>back to the task and clock it out.</li></ol><p>I work most of my using emacs<a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>. Generally the first
thing I do in the morning is opening `org-calendar`. It looks like
this:</p><figure><img src="https://yannesposito.com/posts/0015-how-i-use-org-mode/img/org-super-agenda.png.webp" title="org-super-agenda" alt="Org super calendar view"></img><figcaption aria-hidden="true">Org super calendar view</figcaption></figure><p>Pretty brutalist interface which is a great thing to me. Distraction
free interface going to the essential.</p><p>With this view, I see what I planned to do today. I also see a few
"Due Soon" tasks in case I have the time to handle those.</p><p>When I start working on a task I start a clock on it (I simply type
<code class="verbatim">I</code> when my cursor is on the TODO line).
When I finished some task I change its status from TODO to something
else. Mainly I'm prompted when doing so:</p><pre><code>{ [t] TODO   [p] IN-PROGRESS   [h] HOLD   [w] WAITING
  [d] DONE   [c] CANCELLED     [l] HANDLED }
</code></pre><p>And that's it. The time spent on the task as been clocked I can work
on another task.</p><p>Looking at the agenda view you could notice habits. They start to
become green when you are doing them correctly.</p><p>But generally, I don't use much direct clocking from the agenda. Most
of the time I prefer the capture mechanism. Which bring us to "Workflow
2".</p><h3 id="workflow-2--org-capture-org-refile">Workflow 2: Tracking;
org-capture</h3><p>Most of the tasks I perform on the day are not planned. I have a
generic routine + some prepared events and tasks to performs. But during
the day you have multiple interruptions, and part of my job is to write
code reviews too. I cannot plan those.</p><p>In that case I use <code class="verbatim">org-capture</code> along
<code class="verbatim">org-refile</code>. Mainly <code class="verbatim">org-capture</code> helps you create a new TODO entry.
And <code class="verbatim">org-refile</code> will help you move that
TODO entry to the correct place.</p><p>So let say I get a direct message in the chat asking me to do
something. I generally start org capture (for me it's <code class="verbatim">SPC X</code>). I am presented with the following
choice:</p><pre><code>Select a capture template
=========================

[t] todo
[c] chat
[e] email
[m] meeting
[p] pause
[r] review
[w] work
[i] interruption
[f] chore
---------------------------------------------------------------------------
[q] Abort
</code></pre><p>In my example it was a chat interruption. So I type <code class="verbatim">i</code> that presents me with this</p><pre><code>**** IN-PROGRESS |  :interruption:
:LOGBOOK:
[2020-09-23 Wed 08:01]
ref :: [link-to-where-I-was-in-emacs-when-captured]
</code></pre><p>My cursor placed where the <code class="verbatim">|</code> is
displayed. Here I add the tag <code class="verbatim">chat</code> and a
small description, "dm from John about X" for example. Then I type <code class="verbatim">C-c C-c</code> and the TODO is placed in a <code class="verbatim">tracker.org</code> file under a date tree that looks
like this:</p><pre class="org-mode"><code>* 2020
** 2020-W39
*** 2020-09-21 Monday
*** 2020-09-22 Tuesday
*** 2020-09-23 Wednesday
**** IN-PROGRESS Chat with John about X                          :interruption:chat:
:LOGBOOK:
:END:
[2020-09-23 Wed 17:58]
ref ::
...
</code></pre><p>So the clock for this task started at the moment at made the capture.
In my workflow, I prefer to finish the capture and stop clock later. So
after I finished the capture, the clock is still running while the task
is put in my tracker file.</p><p>Once I finished with that task. I can:</p><ol><li>Jump to the tasks with <code class="verbatim">SPC n o</code> (<code class="verbatim">org-clock-goto</code>), and stop the clock <code class="verbatim">SPC m c o</code> (<code class="verbatim">clock-out</code>).</li><li>Jump to the task and change its status to <code class="verbatim">DONE</code> which will stop the clock.</li><li>Capture another tasks which will stop the clock on the current task
and will start on the new one.</li></ol><p>By the end of the day, my tracker file will contain a date tree with
all the tasks I done in the day. All tasks nicely clocked. I generally
create a clock report that look like this:</p><pre><code>#+BEGIN: clocktable :scope subtree :maxlevel 4 :timestamp t :narrow 36! :match &quot;work&quot;
#+CAPTION: Clock summary at [2020-09-23 Wed 08:20]
| Timestamp              | Headline                             | Time   |   |      |      |
|------------------------+--------------------------------------+--------+---+------+------|
|                        | *Total time*                         | *6:40* |   |      |      |
|------------------------+--------------------------------------+--------+---+------+------|
|                        | \_    2020-09-21 Monday              |        |   | 7:40 |      |
| [2020-09-21 Mon 08:54] | \_      check chat                   |        |   |      | 0:36 |
| [2020-09-21 Mon 09:30] | \_      check reviews                |        |   |      | 0:41 |
| [2020-09-21 Mon 10:11] | \_      check emails                 |        |   |      | 0:07 |
| [2020-09-21 Mon 10:37] | \_      review PR about xxx          |        |   |      | 0:44 |
| [2020-09-21 Mon 11:21] | \_      update my PR from feedbacks  |        |   |      | 0:36 |
| [2020-09-21 Mon 12:08] | \_      review John&#39;s PR about Foo   |        |   |      | 0:12 |
| [2020-09-21 Mon 13:41] | \_      review M&#39;s PR about Bar      |        |   |      | 0:11 |
| [2020-09-21 Mon 13:53] | \_      another thing                |        |   |      | 0:16 |
| [2020-09-21 Mon 14:09] | \_      review PR                    |        |   |      | 0:51 |
| [2020-09-21 Mon 15:00] | \_      work on PR                   |        |   |      | 1:30 |
| [2020-09-21 Mon 16:49] | \_      check another PR             |        |   |      | 0:33 |
| [2020-09-21 Mon 17:03] | \_      answer email                 |        |   |      | 0:55 |
| [2020-09-21 Mon 17:58] | \_      Chat John about X            |        |   |      | 0:28 |

</code></pre><p>And that's mostly it for TODOs and tasks handling.</p><h3 id="workflow-3--org-capture---org-refile">Workflow 3: Add new tasks;
org-capture / org-refile</h3><p>Another thing I do quite often. I need to add new task to be done. Be
it for today or another day.</p><p>In that case, I generally use org-capture again. This time I choose
<code class="verbatim">t</code> for TODO and I generally detail the task
to be done. I add either a SCHEDULE (when I plan to start) or a DEADLINE
(when this must be finished) and I refile it.</p><p>So refile will start a fuzzy search to put this task under some
subtree. So instead of going to my <code class="verbatim">tracker.org</code> file, this goes to my <code class="verbatim">inbox.org</code> file.</p><p>And it will appear in my agenda.</p><h3 id="configuration">Configuration</h3><p>So to have all of that, I added a lot of configuration over time. But
here is the most important part.</p><p>Most of that config is what I personally think are better defaults.
And a minor part of it only is about how I organize myself.</p><div class="sourceCode" id="cb6" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb6-1"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">defun</span><span class="fu"> org-mode-config </span>()</span>
<span id="cb6-2"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;Org-mode.&quot;</span></span>
<span id="cb6-3"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-3" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-extend-today-until <span class="dv">4</span></span>
<span id="cb6-4"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-4" aria-hidden="true" tabindex="-1"></a>        org-use-effective-time <span class="kw">t</span>)</span>
<span id="cb6-5"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-5" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-todo-keywords</span>
<span id="cb6-6"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-6" aria-hidden="true" tabindex="-1"></a>        &#39;((<span class="kw">sequence</span> <span class="st">&quot;TODO(t)&quot;</span></span>
<span id="cb6-7"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-7" aria-hidden="true" tabindex="-1"></a>                    <span class="st">&quot;IN-PROGRESS(p)&quot;</span></span>
<span id="cb6-8"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-8" aria-hidden="true" tabindex="-1"></a>                    <span class="st">&quot;|&quot;</span></span>
<span id="cb6-9"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-9" aria-hidden="true" tabindex="-1"></a>                    <span class="st">&quot;DONE(d)&quot;</span></span>
<span id="cb6-10"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-10" aria-hidden="true" tabindex="-1"></a>                    <span class="st">&quot;HOLD(h@/!)&quot;</span></span>
<span id="cb6-11"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-11" aria-hidden="true" tabindex="-1"></a>                    <span class="st">&quot;CANCELED(c@/!)&quot;</span></span>
<span id="cb6-12"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-12" aria-hidden="true" tabindex="-1"></a>                    <span class="st">&quot;HANDLED(l@/!)&quot;</span>)</span>
<span id="cb6-13"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-13" aria-hidden="true" tabindex="-1"></a>          (<span class="kw">sequence</span> <span class="st">&quot;|&quot;</span> <span class="st">&quot;PAUSE(p)&quot;</span> <span class="st">&quot;CHAT(c)&quot;</span> <span class="st">&quot;EMAIL(e)&quot;</span> <span class="st">&quot;MEETING(m)&quot;</span> <span class="st">&quot;REVIEW(r)&quot;</span> <span class="st">&quot;GEEK(g)&quot;</span>)))</span>
<span id="cb6-14"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-15"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-15" aria-hidden="true" tabindex="-1"></a>  <span class="co">;;; Look &amp; Feel</span></span>
<span id="cb6-16"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-17"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-17" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; I like to have something different than ellipsis because I often use them</span></span>
<span id="cb6-18"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-18" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; myself.</span></span>
<span id="cb6-19"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-19" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-ellipsis <span class="st">&quot; [+]&quot;</span>)</span>
<span id="cb6-20"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-20" aria-hidden="true" tabindex="-1"></a>  (custom-set-faces &#39;(org-ellipsis ((<span class="kw">t</span> (:foreground <span class="st">&quot;gray40&quot;</span> :underline <span class="kw">nil</span>)))))</span>
<span id="cb6-21"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-22"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-22" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">defun</span><span class="fu"> my-org-settings </span>()</span>
<span id="cb6-23"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-23" aria-hidden="true" tabindex="-1"></a>    (org-display-inline-images)</span>
<span id="cb6-24"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-24" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">setq</span> fill-column <span class="dv">75</span>)</span>
<span id="cb6-25"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-25" aria-hidden="true" tabindex="-1"></a>    (abbrev-mode)</span>
<span id="cb6-26"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-26" aria-hidden="true" tabindex="-1"></a>    (org-indent-mode)</span>
<span id="cb6-27"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-27" aria-hidden="true" tabindex="-1"></a>    <span class="kw">nil</span>)</span>
<span id="cb6-28"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-29"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-29" aria-hidden="true" tabindex="-1"></a>  (add-hook <span class="dt">&#39;org-mode-hook</span> <span class="op">#&#39;</span>my-org-settings)</span>
<span id="cb6-30"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-31"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-31" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-tags-column <span class="dv">69</span>)</span>
<span id="cb6-32"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-33"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-33" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; src block indentation / editing / syntax highlighting</span></span>
<span id="cb6-34"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-34" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-src-fontify-natively <span class="kw">t</span></span>
<span id="cb6-35"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-35" aria-hidden="true" tabindex="-1"></a>        org-src-window-setup <span class="dt">&#39;current-window</span> <span class="co">;; edit in current window</span></span>
<span id="cb6-36"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-36" aria-hidden="true" tabindex="-1"></a>        org-src-preserve-indentation <span class="kw">t</span> <span class="co">;; do not put two spaces on the left</span></span>
<span id="cb6-37"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-37" aria-hidden="true" tabindex="-1"></a>        org-src-tab-acts-natively <span class="kw">t</span>)</span>
<span id="cb6-38"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-39"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-39" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; *** Templates</span></span>
<span id="cb6-40"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-40" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; the %a refer to the place you are in emacs when you make the capture</span></span>
<span id="cb6-41"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-41" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; that&#39;s very neat when you do that in an email for example.</span></span>
<span id="cb6-42"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-42" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-capture-templates</span>
<span id="cb6-43"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-43" aria-hidden="true" tabindex="-1"></a>        &#39;((<span class="st">&quot;t&quot;</span> <span class="st">&quot;todo&quot;</span>         entry (file <span class="st">&quot;~/.org/inbox.org&quot;</span>)</span>
<span id="cb6-44"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-44" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* TODO %?</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb6-45"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-45" aria-hidden="true" tabindex="-1"></a>          <span class="co">;; time tracker (clocked tasks)</span></span>
<span id="cb6-46"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-46" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;g&quot;</span> <span class="st">&quot;geek&quot;</span>         entry (file+olp+datetree <span class="st">&quot;~/.org/tracker.org&quot;</span>)</span>
<span id="cb6-47"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-47" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* GEEK %?         :perso:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-48"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-48" aria-hidden="true" tabindex="-1"></a>           :prepend <span class="kw">t</span> :tree-type week :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)</span>
<span id="cb6-49"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-49" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;c&quot;</span> <span class="st">&quot;chat&quot;</span>         entry (file+olp+datetree <span class="st">&quot;~/.org/tracker.org&quot;</span>)</span>
<span id="cb6-50"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-50" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* CHAT %?         :work:chat:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-51"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-51" aria-hidden="true" tabindex="-1"></a>           :prepend <span class="kw">t</span> :tree-type week :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)</span>
<span id="cb6-52"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-52" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;e&quot;</span> <span class="st">&quot;email&quot;</span>        entry (file+olp+datetree <span class="st">&quot;~/.org/tracker.org&quot;</span>)</span>
<span id="cb6-53"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-53" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* EMAIL %?        :work:email:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-54"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-54" aria-hidden="true" tabindex="-1"></a>           :prepend <span class="kw">t</span> :tree-type week :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)</span>
<span id="cb6-55"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-55" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;m&quot;</span> <span class="st">&quot;meeting&quot;</span>      entry (file+olp+datetree <span class="st">&quot;~/.org/tracker.org&quot;</span>)</span>
<span id="cb6-56"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-56" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* MEETING %?      :work:meeting:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-57"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-57" aria-hidden="true" tabindex="-1"></a>           :prepend <span class="kw">t</span> :tree-type week :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)</span>
<span id="cb6-58"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-58" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;r&quot;</span> <span class="st">&quot;review&quot;</span>       entry (file+olp+datetree <span class="st">&quot;~/.org/tracker.org&quot;</span>)</span>
<span id="cb6-59"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-59" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* REVIEW %?       :work:review:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-60"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-60" aria-hidden="true" tabindex="-1"></a>           :prepend <span class="kw">t</span> :tree-type week :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)</span>
<span id="cb6-61"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-61" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;w&quot;</span> <span class="st">&quot;work&quot;</span>         entry (file+olp+datetree <span class="st">&quot;~/.org/tracker.org&quot;</span>)</span>
<span id="cb6-62"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-62" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* IN-PROGRESS %?  :work:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-63"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-63" aria-hidden="true" tabindex="-1"></a>           :prepend <span class="kw">t</span> :tree-type week :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)</span>
<span id="cb6-64"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-64" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;p&quot;</span> <span class="st">&quot;pause&quot;</span>        entry (file+olp+datetree <span class="st">&quot;~/.org/tracker.org&quot;</span>)</span>
<span id="cb6-65"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-65" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* PAUSE %?        :pause:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-66"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-66" aria-hidden="true" tabindex="-1"></a>           :prepend <span class="kw">t</span> :tree-type week :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)</span>
<span id="cb6-67"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-67" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;i&quot;</span> <span class="st">&quot;interruption&quot;</span> entry (file+olp+datetree <span class="st">&quot;~/.org/tracker.org&quot;</span>)</span>
<span id="cb6-68"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-68" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* IN-PROGRESS %?  :interruption:work:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">- ref :: %a</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-69"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-69" aria-hidden="true" tabindex="-1"></a>           :prepend <span class="kw">t</span> :tree-type week :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)</span>
<span id="cb6-70"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-70" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;f&quot;</span> <span class="st">&quot;chore&quot;</span>        entry (file <span class="st">&quot;~/.org/inbox.org&quot;</span>)</span>
<span id="cb6-71"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-71" aria-hidden="true" tabindex="-1"></a>           <span class="st">&quot;* IN-PROGRESS %?  :chore:</span><span class="sc">\n</span><span class="st">%U</span><span class="sc">\n</span><span class="st">&quot;</span></span>
<span id="cb6-72"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-72" aria-hidden="true" tabindex="-1"></a>           :clock-in <span class="kw">t</span> :clock-keep <span class="kw">t</span>)))</span>
<span id="cb6-73"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-73" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-74"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-74" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; How to create default clocktable</span></span>
<span id="cb6-75"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-75" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-clock-clocktable-default-properties</span>
<span id="cb6-76"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-76" aria-hidden="true" tabindex="-1"></a>        &#39;(:scope subtree :maxlevel <span class="dv">4</span> :timestamp <span class="kw">t</span> :link <span class="kw">t</span> :tags <span class="kw">t</span> :narrow <span class="dv">36</span>! :match <span class="st">&quot;work&quot;</span>))</span>
<span id="cb6-77"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-77" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-78"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-78" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; How to display default clock report in agenda view</span></span>
<span id="cb6-79"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-79" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-clockreport-parameter-plist</span>
<span id="cb6-80"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-80" aria-hidden="true" tabindex="-1"></a>        &#39;(:lang <span class="st">&quot;en&quot;</span> :maxlevel <span class="dv">4</span> :fileskip0 <span class="kw">t</span> :link <span class="kw">t</span> :indent <span class="kw">t</span> :narrow <span class="dv">80</span>!))</span>
<span id="cb6-81"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-81" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-82"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-82" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; *** Projectile; default </span><span class="al">TODO</span><span class="co"> file to create in your projects</span></span>
<span id="cb6-83"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-83" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-projectile-file <span class="st">&quot;inbox.org&quot;</span>)</span>
<span id="cb6-84"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-84" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-85"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-85" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; *** Refile mapped to SPC y o r</span></span>
<span id="cb6-86"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-86" aria-hidden="true" tabindex="-1"></a>  (map! :leader :desc <span class="st">&quot;org-refile&quot;</span> <span class="st">&quot;y o r&quot;</span> <span class="op">#&#39;</span>org-refile)</span>
<span id="cb6-87"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-87" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-88"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-88" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; Refile to either the =refile.org= file or to =agenda.org= org =standup.org=</span></span>
<span id="cb6-89"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-89" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-refile-target-files</span>
<span id="cb6-90"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-90" aria-hidden="true" tabindex="-1"></a>        &#39;(<span class="st">&quot;~/.org/tracker.org&quot;</span></span>
<span id="cb6-91"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-91" aria-hidden="true" tabindex="-1"></a>          <span class="st">&quot;~/.org/inbox.org&quot;</span>))</span>
<span id="cb6-92"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-92" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-93"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-93" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-refile-targets</span>
<span id="cb6-94"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-94" aria-hidden="true" tabindex="-1"></a>        &#39;((<span class="kw">nil</span> :maxlevel . <span class="dv">5</span>)</span>
<span id="cb6-95"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-95" aria-hidden="true" tabindex="-1"></a>          (org-refile-target-files :maxlevel . <span class="dv">5</span>)))</span>
<span id="cb6-96"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-96" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-97"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-97" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; *** Agenda</span></span>
<span id="cb6-98"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-98" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-log-into-drawer <span class="kw">t</span>) <span class="co">;; hide the log state change history a bit better</span></span>
<span id="cb6-99"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-99" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-files org-refile-target-files)</span>
<span id="cb6-100"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-100" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-deadline-warning-days <span class="dv">7</span>)</span>
<span id="cb6-101"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-101" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-skip-scheduled-if-deadline-is-shown <span class="kw">t</span>)</span>
<span id="cb6-102"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-102" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-habit-show-habits-only-for-today <span class="kw">nil</span>)</span>
<span id="cb6-103"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-103" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-habit-graph-column <span class="dv">65</span>)</span>
<span id="cb6-104"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-104" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-duration-format <span class="dt">&#39;h</span>:mm) <span class="co">;; show hours at max, not days</span></span>
<span id="cb6-105"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-105" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-compact-blocks <span class="kw">t</span>)</span>
<span id="cb6-106"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-106" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; default show today</span></span>
<span id="cb6-107"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-107" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-span <span class="dt">&#39;day</span>)</span>
<span id="cb6-108"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-108" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-start-day <span class="st">&quot;-0d&quot;</span>)</span>
<span id="cb6-109"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-109" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-start-on-weekday <span class="kw">nil</span>)</span>
<span id="cb6-110"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-110" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-custom-commands</span>
<span id="cb6-111"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-111" aria-hidden="true" tabindex="-1"></a>        &#39;((<span class="st">&quot;d&quot;</span> <span class="st">&quot;Done tasks&quot;</span> tags <span class="st">&quot;/DONE|CANCELED&quot;</span>)</span>
<span id="cb6-112"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-112" aria-hidden="true" tabindex="-1"></a>          (<span class="st">&quot;g&quot;</span> <span class="st">&quot;Plan Today&quot;</span></span>
<span id="cb6-113"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-113" aria-hidden="true" tabindex="-1"></a>           ((agenda <span class="st">&quot;&quot;</span> ((org-agenda-span <span class="dt">&#39;day</span>)))</span>
<span id="cb6-114"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-114" aria-hidden="true" tabindex="-1"></a>            (org-agenda-skip-function &#39;(org-agenda-skip-deadline-if-not-today))</span>
<span id="cb6-115"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-115" aria-hidden="true" tabindex="-1"></a>            (org-agenda-entry-types &#39;(:deadline))</span>
<span id="cb6-116"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-116" aria-hidden="true" tabindex="-1"></a>            (org-agenda-overriding-header <span class="st">&quot;Today&#39;s Deadlines &quot;</span>)))))</span>
<span id="cb6-117"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-117" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-agenda-window-setup <span class="dt">&#39;only-window</span>)</span>
<span id="cb6-118"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-118" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-119"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-119" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">defun</span><span class="fu"> y/go-to-today-agenda </span>()</span>
<span id="cb6-120"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-120" aria-hidden="true" tabindex="-1"></a>    (interactive)</span>
<span id="cb6-121"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-121" aria-hidden="true" tabindex="-1"></a>    (org-agenda <span class="kw">nil</span> <span class="st">&quot;a&quot;</span>))</span>
<span id="cb6-122"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-122" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; Faster jump to agenda today keybinding shortcut (SPC y a)</span></span>
<span id="cb6-123"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-123" aria-hidden="true" tabindex="-1"></a>  (map! :leader</span>
<span id="cb6-124"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-124" aria-hidden="true" tabindex="-1"></a>        :desc <span class="st">&quot;Today&#39;s agenda&quot;</span></span>
<span id="cb6-125"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-125" aria-hidden="true" tabindex="-1"></a>        <span class="st">&quot;y a&quot;</span> <span class="op">#&#39;</span>y/go-to-today-agenda)</span>
<span id="cb6-126"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-126" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-127"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-127" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; ** Org Annotate</span></span>
<span id="cb6-128"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-128" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-129"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-129" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; Ability to take annotate some files, can of double usage with org-capture.</span></span>
<span id="cb6-130"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-130" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; Still, I keep that keyboard shortcut here.</span></span>
<span id="cb6-131"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-131" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; (evil-leader/set-key &quot;oa&quot; &#39;org-annotate-file)</span></span>
<span id="cb6-132"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-132" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-annotate-file-storage-file <span class="st">&quot;~/.org/annotations.org&quot;</span>)</span>
<span id="cb6-133"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-133" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-134"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-134" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-135"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-135" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; ** Org colums</span></span>
<span id="cb6-136"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-136" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; Can be nice sometime to have that column view</span></span>
<span id="cb6-137"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-137" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; give a felling of Excel view</span></span>
<span id="cb6-138"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-138" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-columns-default-format</span>
<span id="cb6-139"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-139" aria-hidden="true" tabindex="-1"></a>        <span class="st">&quot;%TODO %3PRIORITY %40ITEM(Task) %17Effort(Estimated Effort){:} %CLOCKSUM %8TAGS(TAG)&quot;</span>)</span>
<span id="cb6-140"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-140" aria-hidden="true" tabindex="-1"></a>  (map! :leader <span class="st">&quot;y o c&quot;</span> <span class="op">#&#39;</span>org-columns)</span>
<span id="cb6-141"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-141" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-142"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-142" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; ** Deft</span></span>
<span id="cb6-143"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-143" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; useful to find files and jump to them</span></span>
<span id="cb6-144"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-144" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> deft-extensions &#39;(<span class="st">&quot;org&quot;</span> <span class="st">&quot;gpg&quot;</span> <span class="st">&quot;md&quot;</span> <span class="st">&quot;txt&quot;</span>))</span>
<span id="cb6-145"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-145" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> deft-recursive <span class="kw">t</span>)</span>
<span id="cb6-146"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-146" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> deft-use-filter-string-for-filename <span class="kw">t</span>)</span>
<span id="cb6-147"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-147" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> deft-default-extension <span class="st">&quot;org&quot;</span>)</span>
<span id="cb6-148"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-148" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> deft-directory <span class="st">&quot;~/.org&quot;</span>)</span>
<span id="cb6-149"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-149" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-150"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-150" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-151"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-151" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; Org Babel</span></span>
<span id="cb6-152"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-152" aria-hidden="true" tabindex="-1"></a>  (org-babel-do-load-languages</span>
<span id="cb6-153"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-153" aria-hidden="true" tabindex="-1"></a>   <span class="dt">&#39;org-babel-load-languages</span></span>
<span id="cb6-154"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-154" aria-hidden="true" tabindex="-1"></a>   &#39;(<span class="co">;; other Babel languages</span></span>
<span id="cb6-155"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-155" aria-hidden="true" tabindex="-1"></a>     (shell . <span class="kw">t</span>)</span>
<span id="cb6-156"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-156" aria-hidden="true" tabindex="-1"></a>     (http . <span class="kw">t</span>)</span>
<span id="cb6-157"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-157" aria-hidden="true" tabindex="-1"></a>     (clojure . <span class="kw">t</span>)</span>
<span id="cb6-158"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-158" aria-hidden="true" tabindex="-1"></a>     (haskell . <span class="kw">t</span>)</span>
<span id="cb6-159"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-159" aria-hidden="true" tabindex="-1"></a>     (plantuml . <span class="kw">t</span>) <span class="co">;; UML graphs</span></span>
<span id="cb6-160"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-160" aria-hidden="true" tabindex="-1"></a>     (gnuplot . <span class="kw">t</span>)))</span>
<span id="cb6-161"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-161" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-plantuml-jar-path <span class="st">&quot;~/bin/plantuml.jar&quot;</span>))</span>
<span id="cb6-162"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-162" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-163"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-163" aria-hidden="true" tabindex="-1"></a>(use-package! org</span>
<span id="cb6-164"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb6-164" aria-hidden="true" tabindex="-1"></a>  :config (org-mode-config))</span></code></pre></div><p>And also</p><div class="sourceCode" id="cb7" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb7-1"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-1" aria-hidden="true" tabindex="-1"></a>(use-package! org-super-agenda</span>
<span id="cb7-2"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-2" aria-hidden="true" tabindex="-1"></a>  :after org-agenda</span>
<span id="cb7-3"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-3" aria-hidden="true" tabindex="-1"></a>  :custom (org-super-agenda-groups</span>
<span id="cb7-4"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-4" aria-hidden="true" tabindex="-1"></a>           &#39;( <span class="co">;; Each group has an implicit boolean OR operator between its selectors.</span></span>
<span id="cb7-5"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-5" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Overdue&quot;</span> :deadline past :order <span class="dv">0</span>)</span>
<span id="cb7-6"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-6" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Evening Habits&quot;</span> :and (:habit <span class="kw">t</span> :tag <span class="st">&quot;evening&quot;</span>) :order <span class="dv">8</span>)</span>
<span id="cb7-7"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-7" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Habits&quot;</span> :habit <span class="kw">t</span> :order <span class="dv">6</span>)</span>
<span id="cb7-8"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-8" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Today&quot;</span> <span class="co">;; Optionally specify section name</span></span>
<span id="cb7-9"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-9" aria-hidden="true" tabindex="-1"></a>              :time-grid <span class="kw">t</span>  <span class="co">;; Items that appear on the time grid (scheduled/deadline with time)</span></span>
<span id="cb7-10"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-10" aria-hidden="true" tabindex="-1"></a>              :order <span class="dv">3</span>)     <span class="co">;; capture the today first but show it in order 3</span></span>
<span id="cb7-11"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-11" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Low Priority&quot;</span> :priority <span class="st">&quot;C&quot;</span> :tag <span class="st">&quot;maybe&quot;</span> :order <span class="dv">7</span>)</span>
<span id="cb7-12"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-12" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Due Today&quot;</span> :deadline today :order <span class="dv">1</span>)</span>
<span id="cb7-13"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-13" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Important&quot;</span></span>
<span id="cb7-14"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-14" aria-hidden="true" tabindex="-1"></a>              :and (:priority <span class="st">&quot;A&quot;</span> :not (:todo (<span class="st">&quot;DONE&quot;</span> <span class="st">&quot;CANCELED&quot;</span>)))</span>
<span id="cb7-15"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-15" aria-hidden="true" tabindex="-1"></a>              :order <span class="dv">2</span>)</span>
<span id="cb7-16"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-16" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Due Soon&quot;</span> :deadline future :order <span class="dv">4</span>)</span>
<span id="cb7-17"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-17" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Todo&quot;</span> :not (:habit <span class="kw">t</span>) :order <span class="dv">5</span>)</span>
<span id="cb7-18"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-18" aria-hidden="true" tabindex="-1"></a>             (<span class="bu">:name</span> <span class="st">&quot;Waiting&quot;</span> :todo (<span class="st">&quot;WAITING&quot;</span> <span class="st">&quot;HOLD&quot;</span>) :order <span class="dv">9</span>)))</span>
<span id="cb7-19"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-19" aria-hidden="true" tabindex="-1"></a>  :config</span>
<span id="cb7-20"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-20" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">setq</span> org-super-agenda-header-map <span class="kw">nil</span>)</span>
<span id="cb7-21"><a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#cb7-21" aria-hidden="true" tabindex="-1"></a>  (org-super-agenda-mode <span class="kw">t</span>))</span></code></pre></div><h2 id="conclusions">Conclusions</h2><p>That article is already quite long. But if you intend to dig into org
mode, this can be a nice default starting point.</p><p>I haven't really dig into some details but only given you the ability
to start not completely from scratch and with decent default values for
an already advanced usage.</p><p>To resume:</p><ul><li><code class="verbatim">SPC y a</code> ⇒ Show agenda view for
today</li><li><code class="verbatim">SPC X</code> ⇒ Capture a new task, write a
description, then <code class="verbatim">C-c C-c</code>, save that in
<code class="verbatim">tracker.org</code> (or <code class="verbatim">inbox.org</code> depending of the capture
template)</li><li><code class="verbatim">SPC n o</code> ⇒ jump to the current time
tracked tasks</li><li><code class="verbatim">SPC m c o</code> ⇒ stop the clock on that
task; if you capture a new time tracking tasks you don't need to
clock-out</li><li><code class="verbatim">SPC y o r</code> ⇒ <code class="verbatim">org-refile</code>, meaning move that task somewhere
else</li><li><code class="verbatim">SPC q</code> ⇒ add/remove tags to that
task</li></ul><h2 id="footnotes">Footnotes</h2><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr></hr><ol><li id="fn1"><p><em>Short digression</em>: Historically, I coded using
different IDEs. Then I worked for a company that forced me to use
terrible keyboards and after just a few weeks I started to have serious
wrist issues. So to minimize that pain I switched to vim. And it was
<em>awesome</em>. Once you're use to the power of vim keybinding forever
your soul will bound to them. So learning vim is a bit like learning a
new music instrument. You need to construct some muscle memory and
integrate one after one new tricks. Once learned your personal editing
power start to become overwhelming.</p><p>After a few years of vim, I wanted to try to explore new editor
tooling. So I switched to emacs using the spacemacs distribution. So
mainly it's vim but with even better keybindgs, helpers and within
emacs. The main reason for the switch was that vimscript is a really bad
language to configure your editor. Emacs use emacs-LISP. For editor
customization a LISP looked perfect to me. LISP is still one of the most
powerful and easy to use programming language to date.</p><p>And recently, as my personal configuration started to grow so much I
switched to <a href="https://github.com/hlissner/doom-emacs">doom-emacs</a>. I was
quite hesitant to do the switch but so far its been a pleasure. IMHO
using <a href="https://github.com/hlissner/doom-emacs">doom-emacs</a> is
a lot better than using my own personal configuration from scratch
because I wouldn't be able to end up with so much configuration
quality.<a href="https://yannesposito.com/posts/0015-how-i-use-org-mode/index.html#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section></article></main>
]]></description>
</item>


<item>
<title>Auto change emacs theme</title>
<guid>https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html</guid>
<pubDate>Tue, 22 Sep 2020 00:00:00 +0200</pubDate>
<category>emacs</category>
<description><![CDATA[
<main id="content"><article><p>One thing I kind of like is the ability to change emacs theme
depending of the hour. There are two possibilities. One would be to sync
with the sun using the current location. There is an emacs package for
that. It's called <a href="https://github.com/hadronzoo/theme-changer">theme-changer</a>
which at the time of writing those lines is asking for a new maintainer.
This theme changer is very elegant because like macOS use the location
to determine if it is day or night. But I wanted to have more themes
from morning to night:</p><ol><li>early morning: deep yellow (gruvbox-light),</li><li>morning: light yellow (solarized-light),</li><li>day: grey/blueish during the day (nord-light),</li><li>evening: deep yellow again (gruvbox-light)</li><li>night: dark theme (oceanic-next)</li><li>sleep time: neon-like (laserwave)</li></ol><p>And also, I wanted that to follow my working hours and not really the
sun. I might change my mind and use the code of theme-changer to follow
the curve of the sun. But for now, just using straight hours should be
good enough. So here is my piece of code I added to my doom-emacs <code class="verbatim">config.el</code>:</p><div class="sourceCode" id="cb1" data-org-language="emacs-lisp"><pre class="sourceCode commonlisp"><code class="sourceCode commonlisp"><span id="cb1-1"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">defun</span><span class="fu"> y/auto-update-theme </span>()</span>
<span id="cb1-2"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;depending on time use different theme&quot;</span></span>
<span id="cb1-3"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="co">;; very early =&gt; gruvbox-light, solarized-light, nord-light</span></span>
<span id="cb1-4"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-4" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">let*</span> ((hour (<span class="kw">nth</span> <span class="dv">2</span> (decode-time (current-time))))</span>
<span id="cb1-5"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-5" aria-hidden="true" tabindex="-1"></a>         (theme (<span class="kw">cond</span> ((<span class="op">&lt;=</span> <span class="dv">7</span> hour <span class="dv">8</span>)   <span class="dt">&#39;doom-gruvbox-light</span>)</span>
<span id="cb1-6"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-6" aria-hidden="true" tabindex="-1"></a>                      ((<span class="op">=</span> <span class="dv">9</span> hour)      <span class="dt">&#39;doom-solarized-light</span>)</span>
<span id="cb1-7"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-7" aria-hidden="true" tabindex="-1"></a>                      ((<span class="op">&lt;=</span> <span class="dv">10</span> hour <span class="dv">16</span>) <span class="dt">&#39;doom-nord-light</span>)</span>
<span id="cb1-8"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-8" aria-hidden="true" tabindex="-1"></a>                      ((<span class="op">&lt;=</span> <span class="dv">17</span> hour <span class="dv">18</span>) <span class="dt">&#39;doom-gruvbox-light</span>)</span>
<span id="cb1-9"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-9" aria-hidden="true" tabindex="-1"></a>                      ((<span class="op">&lt;=</span> <span class="dv">19</span> hour <span class="dv">22</span>) <span class="dt">&#39;doom-oceanic-next</span>)</span>
<span id="cb1-10"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-10" aria-hidden="true" tabindex="-1"></a>                      (<span class="kw">t</span>               <span class="dt">&#39;doom-laserwave</span>))))</span>
<span id="cb1-11"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-11" aria-hidden="true" tabindex="-1"></a>    (<span class="kw">when</span> (<span class="kw">not</span> (<span class="kw">equal</span> doom-theme theme))</span>
<span id="cb1-12"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-12" aria-hidden="true" tabindex="-1"></a>      (<span class="kw">setq</span> doom-theme theme)</span>
<span id="cb1-13"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-13" aria-hidden="true" tabindex="-1"></a>      (load-theme doom-theme <span class="kw">t</span>))</span>
<span id="cb1-14"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-14" aria-hidden="true" tabindex="-1"></a>    <span class="co">;; run that function again next hour</span></span>
<span id="cb1-15"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-15" aria-hidden="true" tabindex="-1"></a>    (run-at-time (<span class="kw">format</span> <span class="st">&quot;%02d:%02d&quot;</span> (<span class="op">+</span> hour <span class="dv">1</span>) <span class="dv">0</span>) <span class="kw">nil</span> <span class="dt">&#39;y/auto-update-theme</span>)))</span>
<span id="cb1-16"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-17"><a href="https://yannesposito.com/posts/0014-change-emacs-theme-automatically/index.html#cb1-17" aria-hidden="true" tabindex="-1"></a>(y/auto-update-theme)</span></code></pre></div><p>I'm still playing with it. So there still might be a bug. Use at your
own risk. Happy hacking to all of you.</p></article></main>
]]></description>
</item>


<item>
<title>How to choose your tools</title>
<guid>https://yannesposito.com/posts/0013-how-to-choose-your-tools/index.html</guid>
<pubDate>Sat, 09 May 2020 00:00:00 +0200</pubDate>
<category>emacs</category>
<category>softwares</category>
<description><![CDATA[
<main id="content"><article><p>This week I didn't take a look at HN to grab some news. And this
week-end, in the morning I read those:</p><ul><li><a href="https://news.ycombinator.com/item?id=23102430">Zoom
acquires keybase</a></li><li><a href="https://news.ycombinator.com/item?id=23107123">Making Emacs
popular again</a></li><li><a href="https://news.ycombinator.com/item?id=23092904">Github
Codespace</a></li></ul><figure><img src="https://yannesposito.com/posts/0013-how-to-choose-your-tools/Welcome-to-Halsingland.jpg.webp" title="Welcome to Halsingland" alt="Midsommar Welcome"></img><figcaption>Welcome to Halsingland</figcaption></figure><p>Similar articles have existed for years on different products. What
is their common point? <em>Software tooling and their potential change
and disappearance</em>.</p><p>Across the years, too many times I saw tools disappear. By tools I
mean applications, web applications, web sites. I think we can also
include programming languages, control versioning tools, building tools,
package manager, etc…</p><p>The story can be quite different. Sometimes the disappearance of a
tool is positive, because I found a better one (from cvs to svn to git).
But, too often, the tool simply disappears or worse downgrade its
quality. I think we can find different names for those softwares:</p><ul><li><em>bloatware</em>: remember digg, stumbleupon, windows?</li><li><em>downgradeware</em>: Swagger-UI v3 (v2 is neat), reddit new
redesign (looks better, but slow)</li><li><em>payware</em>: Useful free service ask for money now. Or cost a
lot more.</li><li><em>crapware</em>: Stop to works, quality degrate unless you pay:
Twitter streaming API?</li><li><em>dieware</em>: Remember Friendfeed, Google Reader™, etc…</li><li>etc…</li></ul><p>This is often quite frustrating because you lose a lot of your
investment with that tool.</p><p>Regarding Github Codespace; the integration of VSCode™ inside GitHub™
can be even worse. This is what I would call a <em>trapware</em>.</p><div class="notes"><p><em>trapware</em>: A software that is intended to put you inside a
closed ecosystem. By slowly but surely add features that while looking
great for the user at first sight will prevent interoperability with
other tools.</p></div><p>Furthermore, the fact that Microsoft is involved give this story a
taste of <a href="https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish">Embrace,
Extend and Extinguish</a>.</p><p>My real concern is that it could become a <em>work framework</em>.
This could impose the full tooling on a lot of developers without giving
them the freedom of choice.</p><p>For a startup CTO/CEO this GitHub™ Codespace™ could offer the
following advantages:</p><ul><li><em>security</em>: impossible or very hard to steal the source code
by a single dev.</li><li><em>homogeneity</em>: all dev must use the same development
environment. Thus the integration of new dev is faster.</li><li><em>cheaper</em>: don't need to pay for a full featured, fast
machine to each new developer. A less performant machine able to display
an electron app will do the trick.</li><li><em>stats</em>: you can observe the throughput of your developers.
How many commits a day, how many lines of code, etc… How much bugs
involved which part of the code and thus which dev to blame? How much
time the dev is typing, moving its mouse, how much copy/paste is
involved, etc…</li></ul><p>For the single developers and open source developers this offer:</p><ul><li><em>homogeneity</em>: if I learn how to work in this environment,
I'll be easier to recruit and I'll know how to work fast.</li><li><em>lower barrier to entry</em>: for an opensource repository, it
will become much easier for anyone to propose a PR to fix some issue. No
need to local clone the project, no need to download all the
dependencies to test it locally, etc…</li></ul><p>But the price to pay is hidden.</p><figure><img src="https://yannesposito.com/posts/0013-how-to-choose-your-tools/midsommar-cry.jpg.webp" alt="Midsommar Sorrow"></img><figcaption>Midsommar Sorrow</figcaption></figure><ol><li>First, you are now, not able to choose your local working
environment on your machine.</li><li>GitHub™ can still change so much to become one of the previously
mentionned <code>/.*ware/</code> you don't want to be involved with.
They could forces you to pay a lot more, remove features, redesign to a
bloatware, make it harder to interop with other platforms (prefer Azure
to AWS etc…).</li><li>If everything involve machines in the cloud via the browser and via
authorized plugins only. A lot of tools, features will never be allowed
in this new ecosystem.</li><li>Surveillance on meaningless or wrong metrics about your work.
Instead of being evaluated on the feature you shipped or on other higher
level metrics. It will be very tempting for your bosses to find flaws in
your working habits. We are already living in a world were surveillance,
metrics and stats are too easy to grab about a person. And anyone
involved know this is all bullshit. Human are very good to play those
kind of games. So people really working hard for the best will certainly
perform badly compared to other people that simply trick the
system.</li></ol><p>So as good as Codespace can be, I think this warning is good to keep
that in mind. Don't put yourself in a trap.</p><p>The <a href="https://news.ycombinator.com/item?id=23102430">Zoom
acquires keybase</a> is just another story of a dying product.
Apparently the keybase team will probably stop maintaining keybase. The
idea behind keybase was pretty nice. And they filled a gap in the
current open source world.</p><p>The last article I mentionned was <a href="https://news.ycombinator.com/item?id=23107123">Making Emacs
popular again</a>. The first comment in HN was about how VSCode is easy
to start with as compared to Emacs that need a lot more time to
configure correctly for your needs. Yes, VSCode certainly just work and
is easy to use. But Emacs is another beast. VSCode can become bad very
fast, you don't control how it will evolve. The fact that this is a
succesful Microsoft product does not garanty it will keep its currently
quality. Emacs on the other hand is 44 year old and was designed so that
it adapts to you. You are the one using libs and customizing it.</p><p>The argument to chose VSCode instead of Emacs look similar to me to
the debate "Frameworks vs Libraries". Frameworks are easier to start
with, but soon you find corner cases were you start to fight against
them.</p><p>A Library on the other hand, is just a bunch of helpers you can use.
And if you need another functionality, just make it using the libraries.
But you have a lot more work to do yourself.</p><p>The common pattern I see during choice decision is often reducible
to:</p><ol><li>Easy now, but less extensible and harder in the long run.</li><li>Harder now, but more extensible and easier in the long run.</li></ol><p>As a conclustion I would state that when you need to choose between
different tools. Take the time to think about the investment costs.
Sometime, the bit of pain in the begining is worth it. In particular if
you are going to use this tool every days for many hours during the
following years. If on the other hand you don't plan to use that tool
much. Going with the easy option is certainly the best choice.</p><p>I consider Emacs to be of the 2nd option when compared to VSCode.
Harder to start, but with a lot more control and potential power that
you will probably never be able to get with most modern IDE/Editor. Also
choosing a Free Software<a href="https://yannesposito.com/posts/0013-how-to-choose-your-tools/index.html#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> gives you a lot more control about
its future.</p><h2 id="post-conclusion">Post-conclusion – Emacs is awesome</h2><figure><img src="https://yannesposito.com/posts/0013-how-to-choose-your-tools/midsommar-joy.jpg.webp" alt="Midsommar Joy"></img><figcaption>Midsommar Joy</figcaption></figure><p>To go beyond my opinion, I'd like to share my experience with editors
and emacs.</p><p>When I started to be serious about coding, I was taught to use vi,
not vim, vi. I only knew a few survival vi commands: <code class="verbatim">i</code>, <code class="verbatim">a</code>, <code class="verbatim">dd</code> and <code class="verbatim">cw</code>. A few
years later I started to use IDEs and I was thrilled. A few years
forward I started to work for a company that forced me to use their
shitty computers. Quite soon, I started to have wrist issues. Thus I
decided to use vim again but be serious about it this time. And I saw
the benefits only after a few weeks. They were tremendous. No more wrist
pain. And an incredible edition power at the tip of my fingers<a href="https://yannesposito.com/posts/0013-how-to-choose-your-tools/index.html#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>.</p><p>Then, I started a new job where we decided to code in Clojure. Of
course Clojure being a LISP and emacs using also a LISP as script
language, it sound natural to try Emacs even though I loved Vim. I
started by installing spacemacs. At that time I didn't want to invest
much time in learning Emacs. I just wanted to learn the tricks that will
make Emacs more valuable to my work. It did after just a few days or
maybe weeks. I used Emacs superficially for years. This was already
quite efficient, at least as much as vim.</p><p>Recently I dug deeper. I heard much praise about org-mode and I
became curious. I discovered why it is so great. Basic org-mode is
already quite valuable. But if you dig, it starts to be awesome.
Unfortunately this is a bit hard to describe how org-mode is great
without really digging a bit.</p><p>You can think of org-mode as an extremely versatile todo-list and
note taker with agenda and time tracking integration. You are in deep
control of your workflow. But mainly here are a few example of usages
that are really worth it:</p><ul><li>note taker</li><li>documentation; this is a far better than markdown</li><li>interactive document; run code inside the doc, keep track of the
results</li><li>export to HMTL/PDF</li><li>time tracking</li><li>reminders</li></ul><p>Recently there is also org-roam that is a step further to make
orgmode a nice place to keep track of all your knowledge in one place.
You can take a look at this <a href="https://www.youtube.com/watch?v=Lg61ocfxk3c">great video</a> by
Matt Williams.</p><p>Emacs changed my workflow by making me more productive. It improved
not only my coding workflow, but my full work environment. I started
with the editor, a few plugins, and slowly, I integrated more aspect of
my day to day tasks in emacs. Emacs is designed to adapt to your own
needs. As such it is a lot easier to automate a lot of small tasks.</p><p>I really love Emacs and if you want to joyfully join the Emacs users
here are my advices:</p><p>Start by using either <a href="https://www.spacemacs.org">spacemacs</a> or <a href="https://github.com/hlissner/doom-emacs">doom-emacs</a>. It will
take a few weeks to absorb vim keybindings. Slowly you'll start to learn
how to configure it for your needs.</p><p>I really advise you to take a look at org-mode. Mastering it could
change your carrier. Im my opinion <a href="https://orgmode.org">org-mode</a> alone is a good reason enough to
use emacs. But there are a lot more to discover.</p><p>However, if you are used to tools from startups, with nice UI/UX.
Almost no configuration cost. Be aware that digging in Free Softwares is
a lot different. Instead of having a big bundle with everything prepared
to work you you will need to take the time to configure each part of a
big system separately.</p><p>Howevery I'm deeply convinced the investment is really worth it.</p><section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes"><hr></hr><ol><li id="fn1"><p>note I said <em>free software</em> and not <em>open
source</em>; c.f <a href="https://www.gnu.org/philosophy/open-source-misses-the-point.en.html">Why
Open Source misses the point of Free Software</a><a href="https://yannesposito.com/posts/0013-how-to-choose-your-tools/index.html#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li><li id="fn2"><p><a href="http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/">Lear
Vim Progressively</a> is an old "popular" blog post of mine.<a href="https://yannesposito.com/posts/0013-how-to-choose-your-tools/index.html#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li></ol></section></article></main>
]]></description>
</item>

</channel>
</rss>
