<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Agustín Mista</title>
    <link href="https://www.mista.me/atom.xml" rel="self" />
    <link href="https://www.mista.me" />
    <id>https://www.mista.me/atom.xml</id>
    <author>
        <name>Agustín Mista</name>
        
        <email>agustin@mista.me</email>
        
    </author>
    <updated>2022-11-30T00:00:00Z</updated>
    <entry>
    <title>Dusting the bookshelf</title>
    <link href="https://www.mista.me/blog/bibtool/index.html" />
    <id>https://www.mista.me/blog/bibtool/index.html</id>
    <published>2022-11-30T00:00:00Z</published>
    <updated>2022-11-30T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>I’m currently in the process of writing my Ph.D. thesis. This means I need to gather all my published papers and fight against LaTeX and Lhs2TeX until everything fits together and looks somewhat decent. One of the many challenges of this process was to deal with the multiple bibliography (<code>.bib</code>) files I collected over the years. Hopefully I’m not the only one in this situation:</p>
<ul>
<li>Paper <span class="math inline">0</span>: inherit a gigantic <code>.bib</code> from advisor and add new references as needed</li>
<li>Paper <span class="math inline"><em>n</em></span>: pick the <code>.bib</code> from some paper <span class="math inline"><em>k</em></span> (with <span class="math inline">0 ≤ <em>k</em> &lt; <em>n</em></span>) and add new references as needed</li>
</ul>
<p>So here I was, sitting with ~50kLOC worth of <code>.bib</code> files I needed to organize. On the other hand, I wanted to collect all the references in a single section of the thesis to avoid repetetition and inconsistencies. So, in principle I could have simply used the good ’ol <code>bibtool</code> utility to merge all my <code>.bib</code> files together:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> bibtool <span class="at">-s</span> foo.bib bar.bib baz.bib</span></code></pre></div>
<p>The problem with this approach is that I would have ended up again with a gigantic <code>.bib</code> file when, in reality, I only needed a handful of entries from it. To solve this, I put together a small bash script that pulls references from <code>.bib</code> files on demand. The trick is to ask for forgiveness rather than for permission:</p>
<ol type="1">
<li>Compile the project with an empty <code>.bib</code> file</li>
<li>Parse the log file to find which citations are missing references</li>
<li>Extract each of those references from some existing <code>.bib</code> file and append them to the project’s <code>.bib</code> file</li>
<li>Compile the project again, now without missing references :D</li>
</ol>
<h2 id="finding-missing-citations">Finding missing citations</h2>
<p>If we inspect the log files created by <code>pdflatex</code>/<code>bibtex</code>, missing citations are reported as:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">LaTeX</span> Warning: Citation <span class="st">'afl'</span> on page 21 undefined on input line 16664.</span></code></pre></div>
<p>So we need to find these warnings, extract the citation keys (i.e., <code>afl</code> in the line above) from them and remove any duplicates:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="va">LOGFILE</span><span class="op">=</span>thesis.log</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="va">MISSING</span><span class="op">=</span><span class="va">$(</span> <span class="dt">\</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="fu">grep</span> <span class="st">&quot;LaTeX Warning: Citation&quot;</span> <span class="va">$LOGFILE</span> <span class="kw">|</span><span class="dt">\</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="fu">awk</span> <span class="at">-F</span><span class="st">' '</span> <span class="st">'{print $4}'</span> <span class="kw">|</span> <span class="fu">tr</span> <span class="at">-d</span> <span class="dt">\'</span> <span class="kw">|</span><span class="dt">\</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">sort</span> <span class="kw">|</span> <span class="fu">uniq</span> <span class="dt">\</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="va">)</span></span></code></pre></div>
<p>With the culprits at hand, we can move onto the next step.</p>
<h2 id="retrieving-citations-on-demand">Retrieving citations on demand</h2>
<p>To find if a concrete reference exists in a given citation file, we can use <code>bibtool</code>’s <code>select</code> resource:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">find_ref ()</span> <span class="kw">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="ex">bibtool</span> <span class="at">-q</span> <span class="at">-r</span> biblatex <span class="st">'--expand.macros=ON'</span> <span class="st">'--print.all.strings=OFF'</span> <span class="st">'--select{$key &quot;'</span><span class="va">$1</span><span class="st">'&quot;}'</span> <span class="va">$2</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div>
<p>This way, <code>find_ref $key $bibfile</code> will try to find the reference <code>$key</code> in the file <code>$bibfile</code>, returning either its corresponding entry if it can find it, or an empty string otherwise. The <code>-r biblatex</code> option is needed to allow indexing <code>@online</code> entries, whereas the <code>expand.macros</code> and <code>print.all.strings</code> options are needed to ensure that string macros are expanded, so we don’t need to print them everytime we look for a citation key.</p>
<p>Then, we can iterate over the existing <code>.bib</code> files to see if we find each missing citation:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="va">BIBFILES</span><span class="op">=</span><span class="va">$(</span><span class="fu">ls</span> bib/<span class="pp">*</span>.bib<span class="va">)</span> <span class="co"># Existing .bib files</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="va">OUTPUT</span><span class="op">=</span>references.bib <span class="co"># The project's .bib file</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> missing <span class="kw">in</span> <span class="va">$MISSING</span><span class="kw">;</span> <span class="cf">do</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>  <span class="bu">echo</span> <span class="at">-n</span> <span class="st">&quot;Looking for </span><span class="va">$missing</span><span class="st">: &quot;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>  <span class="co"># Reference is already there</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>  <span class="va">FOUND</span><span class="op">=</span><span class="va">$(</span><span class="ex">find_ref</span> <span class="va">$missing</span> <span class="va">$OUTPUT)</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="bu">[</span> <span class="ot">!</span> <span class="ot">-z</span> <span class="st">&quot;</span><span class="va">$FOUND</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;Already in in </span><span class="va">$OUTPUT</span><span class="st">!&quot;</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">continue</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>  <span class="cf">fi</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>  <span class="co"># Reference is in some .bib file</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>  <span class="cf">for</span> bibfile <span class="kw">in</span> <span class="va">$BIBFILES</span><span class="kw">;</span> <span class="cf">do</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>    <span class="va">FOUND</span><span class="op">=</span><span class="va">$(</span><span class="ex">find_ref</span> <span class="va">$missing</span> <span class="va">$bibfile)</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="bu">[</span> <span class="ot">!</span> <span class="ot">-z</span> <span class="st">&quot;</span><span class="va">$FOUND</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>      <span class="bu">echo</span> <span class="st">&quot;Found in </span><span class="va">$bibfile</span><span class="st">!&quot;</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>      <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">$FOUND</span><span class="st">&quot;</span> <span class="op">&gt;&gt;</span> <span class="va">$OUTPUT</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a>      <span class="cf">break</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>    <span class="cf">fi</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a>  <span class="cf">done</span></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a>  <span class="co"># Reference is M.I.A.</span></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="bu">[</span> <span class="ot">-z</span> <span class="st">&quot;</span><span class="va">$FOUND</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;Could not find any entry!&quot;</span></span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a>  <span class="cf">fi</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a><span class="cf">done</span></span></code></pre></div>
<p>The reason we first look in the project’s <code>.bib</code> file is to avoid producing duplicate entries when:</p>
<ul>
<li>The user already added them manually, or</li>
<li>We have an overlapping entry key that matches a previously searched missing reference. <a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></li>
</ul>
<h2 id="trying-it-out">Trying it out</h2>
<p>We can now run this little snippet to gather only the references we need:</p>
<pre><code>Looking for afl: Found in bib/foo.bib!
Looking for asan: Found in bib/bar.bib!
Looking for peach: Found in bib/baz.bib!
...</code></pre>
<p>And my curated <code>.bib</code> file now has “only” a little over 2000 lines of code ¯\_(ツ)_/¯</p>
<p>You can find the bash script <a href="../assets/code/find_references.sh">here</a> in case you want to give it a try.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>This happens because <code>bibtool</code>’s <code>select</code> resource looks for <strong>all</strong> the entries whose keys <strong>contain</strong> the given key string. So, for instance, both keys <code>foo</code> and <code>foobar</code> will match with <code>foo</code>, possibly returning more than one entry per <code>find_ref</code>. If you know how to avoid this, please let me know!<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>Persistent cabal store in Haskell devcontainers</title>
    <link href="https://www.mista.me/blog/devcontainer-cabal-store/index.html" />
    <id>https://www.mista.me/blog/devcontainer-cabal-store/index.html</id>
    <published>2023-01-14T00:00:00Z</published>
    <updated>2023-01-14T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>This is going to be a short one. I recently added a <a href="https://code.visualstudio.com/docs/devcontainers/containers">development container</a> definition to the repository that hosts this very website. The definition I’m using is taken from <a href="https://github.com/microsoft/vscode-dev-containers/tree/main/containers/haskell">Microsoft’s devcontainer repository</a> via the option integrated in Visual Studio Code<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</p>
<p>For the most part, this development container works flawelessly. Except for this: the user’s cabal folder (located at <code>~/.cabal</code> by default) is not persisted across container runs. This means I need to rebuild from scratch most of the dependencies that cabal saves in its user-wide package store (<code>~/.cabal/store</code>) every time I reopen the project (notably annoying with Hakyll’s dependency of big-chungus <code>pandoc</code>).</p>
<p>There are a couple of ways to solve this:</p>
<ul>
<li><p>By configuring cabal to use a package store located inside the repository’s folder (which is bind-mounted to the host’s file system).</p>
<p><em>Problem</em>: this would duplicate the work for any dependency shared across Haskell projects.</p></li>
<li><p>Creating a <code>~/.cabal</code> folder in my host computer and share it among devcontainers.</p>
<p><em>Problem</em>: this folder would be continuously modified by <code>root</code>, a permission hell waiting to happen.</p></li>
<li><p>Mounting the package store to a <a href="https://code.visualstudio.com/remote/advancedcontainers/improve-performance#_use-a-targeted-named-volume">Docker named volume</a>.</p>
<p>This is what I ended up doing, as it takes the least work and doesn’t clutter my host’s <code>$HOME</code>.</p>
<p>The only changes needed are adding the following properties to <code>.devcontainer/devcontainer.json</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="er">&quot;mounts&quot;:</span> <span class="ot">[</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;source=cabal_store,target=/home/vscode/.cabal/store,type=volume&quot;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="ot">]</span><span class="er">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="er">&quot;postCreateCommand&quot;:</span> <span class="er">&quot;sudo</span> <span class="er">chown</span> <span class="er">vscode</span> <span class="er">/home/vscode/.cabal/store&quot;</span></span></code></pre></div>
<p>Where <code>cabal_store</code> is the name of the Docker volume to be created and <code>vscode</code> is the Linux user of this devcontainer. The <code>postCreateCommand</code> is there to make sure our container can access and modify the package store (only needed in case your devcontainer logs in to a user other than <code>root</code>). Repeat this for every Haskell devcontainer you want to share the cabal store with.</p></li>
</ul>
<p>That’s it! Signing off.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Sadly, I found today this repo is getting deprecated in favor of <a href="https://containers.dev/">containers.dev</a>, and the latter doesn’t yet host any devcontainer definition for Haskell.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>Creating CI jobs dynamically in GitHub</title>
    <link href="https://www.mista.me/blog/dymamic-jobs-github-actions/index.html" />
    <id>https://www.mista.me/blog/dymamic-jobs-github-actions/index.html</id>
    <published>2023-01-15T00:00:00Z</published>
    <updated>2023-01-15T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>This a neat trick I learnt the other day while I was writing some automation for my <a href="https://github.com/agustinmista/qmk_playground">out-of-tree QMK builder</a> project. There, I have firmware files for different keyboards in a folder called <code>keyboards</code>:</p>
<pre><code>Makefile
keyboards
  |-- preonic
  |     |-- keymap.c
  |     |-- config.h
  |     |-- rules.mk
  |     \-- env
  \-- thekey_v2
        |-- keymap.c
        |-- config.h
        |-- rules.mk
        \-- env</code></pre>
<p>The content of these folders is not important today (but maybe soon). What’s relevant here is that I have a Makefile that builds a given firmware by passing the <code>KBD</code> variable with the folder where it’s defined:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> make KBD=preonic</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> make KBD=thekey_v2</span></code></pre></div>
<p>Now, if I want to build and publish these firmares in CI, I could simply do one after another, something like:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">on</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="at">push</span><span class="kw">]</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="fu">jobs</span><span class="kw">:</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">build</span><span class="kw">:</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Checkout repository</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v3</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Build preonic firmware</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">run</span><span class="kw">:</span><span class="at"> make KBD=preonic</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Build thekey_v2 firmware</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">run</span><span class="kw">:</span><span class="at"> make KBD=thekey_v2</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Create artifact</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/upload-artifact@v2</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a><span class="at">          </span><span class="fu">name</span><span class="kw">:</span><span class="at"> firmwares</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a><span class="fu">          path</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a>            build/*.bin</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a>            build/*.hex</span></code></pre></div>
<p>The problem with this approach is that I need to remember to go and change the CI workflow everytime keyboard N+1 suddenly appears. What we want instead is to run something like:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> kbd <span class="kw">in</span> <span class="va">$(</span><span class="fu">ls</span> keyboards<span class="va">)</span><span class="kw">;</span> <span class="fu">make</span> KBD=<span class="va">$kbd</span><span class="kw">;</span> <span class="cf">done</span></span></code></pre></div>
<p>One interesting way to do this is to split the workflow into:</p>
<ol type="1">
<li>A <code>find-targets</code> job that “discovers” which jobs to run and saves them in an output named <code>targets</code>.</li>
<li>A <code>build</code> job that reads these <code>targets</code> and uses the <a href="https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs"><code>matrix</code> strategy</a> to run them in parallel.</li>
</ol>
<p>This looks like:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">on</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="at">push</span><span class="kw">]</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="fu">jobs</span><span class="kw">:</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">find-targets</span><span class="kw">:</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">outputs</span><span class="kw">:</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">targets</span><span class="kw">:</span><span class="at"> ${{ steps.set-targets.outputs.targets }}</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v2</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">id</span><span class="kw">:</span><span class="at"> set-targets</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">run</span><span class="kw">:</span><span class="at"> echo &quot;targets=$(ls keyboards | jq -R '[.]' | jq -s -c 'add')&quot; &gt;&gt; $GITHUB_OUTPUT</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">build</span><span class="kw">:</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">needs</span><span class="kw">:</span><span class="at"> find-targets</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">strategy</span><span class="kw">:</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">matrix</span><span class="kw">:</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">KBD</span><span class="kw">:</span><span class="at"> ${{ fromJson(needs.find-targets.outputs.targets) }}</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Checkout repository</span></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v3</span></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Build firmware</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">run</span><span class="kw">:</span><span class="at"> make KBD=${{ matrix.KBD }}</span></span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Create artifact</span></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/upload-artifact@v2</span></span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a><span class="at">          </span><span class="fu">name</span><span class="kw">:</span><span class="at"> ${{ matrix.KBD }}</span></span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a><span class="fu">          path</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a>            build/*.bin</span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a>            build/*.hex</span></code></pre></div>
<p>There are a couple of things worth mentioning here:</p>
<ol type="1">
<li><p>The actual build step is more complicated as it uses the official <code>qmkfm/qmk_cli</code> Docker image to build the firmwares. The code I show here is deliberately simpler to show the idea.</p></li>
<li><p>We need the <code>build</code> job to depend on <code>find-targets</code> so they run in the correct order. This is easy to enforce by adding <code>needs: find-targets</code> as shown above.</p></li>
<li><p>In <code>find-targets</code>, we need to create a JSON array of targets, e.g. <code>["preonic","thekey_v2"]</code>. For this, the <code>set-targets</code> step first lists the files under <code>keyboards</code> and progressively add them to an empty array using good ’ol <code>jq</code>. This array is then saved to the <code>GITHUB_OUTPUT</code> environment variable associated with this step. Finally, this job retrieves the targets from the output of the <code>set-targets</code> and assigns it to the job’s output <code>targets</code>. For reference, this is the new and cool way to do this now that <a href="https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/"><code>save-output</code> is getting deprecated</a>.</p></li>
<li><p>In <code>build</code>, we define the build matrix by retrieving the <code>targets</code> variable from output of <code>find-targets</code>. GitHub will then run the <code>build</code> action once per target, instantiating the <code>matrix.KBD</code> variable with the current target name, which we use later to call <code>make</code> accordingly.</p></li>
</ol>
<p>With this in place, GitHub will create <code>build</code> jobs dinamycally on push, and there’s no need to hardcode build targets anywhere :)</p>]]></summary>
</entry>
<entry>
    <title>Hello, world!</title>
    <link href="https://www.mista.me/blog/hello/index.html" />
    <id>https://www.mista.me/blog/hello/index.html</id>
    <published>2022-11-25T00:00:00Z</published>
    <updated>2022-11-25T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>Hello there!</p>
<p>It’s been a very long time since I originally built this website with the sole purpose of having my publications in a single place. While doing so, I also found it was quite easy to add the necessary code to generate blog posts (like the one you’re looking at now). So, <em>after all, why not, why shouldn’t I</em>? To the surprise of anybody, I haven’t written anything in the 4 years this site has been up — even though I really wanted to!</p>
<p>Now, the reasons for not having written anything in 4 years despite really wanting to are probably unsurprising: sometimes I feel I have nothing interesting to say. Other times when I find something interesting that I think I could write about, my immediate reaction is to think “why anybody would want to read about this”. Sometimes I feel the effort needed to write something meaningful isn’t really worth it. Sometimes I’m just tired and I rather zoom out and binge-watch a TV show or play a videogame than write a long existential blob. You know, all the bits in the procrastinator’s army knife.</p>
<p>But one day (yesterday actually), I stumbled upon <a href="https://tinyprojects.dev/">TinyProjects</a>, a small website full of fun week-long projects. Along with that, Ben (the author) also writes daily posts as a sort of journal. After seeing some of his posts, I fell in love with the idea of writing shorter, more frequent, and more open-ended posts. So that’s what I will try, too.</p>
<p>But how often should I write before this becomes a chore? Daily is perhaps too much of a commitment, but monthly is definitely too little. Something between these two sounds reasonable, but I’d like to have a more concrete (however arbitrary) timescale to start feeling I’m getting behind. So here’s one: this website’s <a href="https://github.com/agustinmista/website">source code</a> is built and deployed by a GitHub Action. Its code is generated by <a href="https://jaspervdj.be/hakyll/">Hakyll</a>. Hakyll depends on <a href="https://pandoc.org/">Pandoc</a> to translate Markdown into HTML. Pandoc takes some 40 minutes to build inside my GitHub Action, and it would be shameful to waste 40+ minutes of CI time building Hakyll+Pandoc for every new silly post I create. Of course, my CI action uses a cache to keep everything hot’n’ready. But there’s a <a href="https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy">limit</a> to that: GitHub caches action’s dependencies for only 7 days.</p>
<p>So that’s my target, to write stuff often enough to keep alive my website’s CI cache :)</p>]]></summary>
</entry>
<entry>
    <title>It's over</title>
    <link href="https://www.mista.me/blog/itisover/index.html" />
    <id>https://www.mista.me/blog/itisover/index.html</id>
    <published>2023-06-19T00:00:00Z</published>
    <updated>2023-06-19T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>Today I’m thrilled to share that I succesfully defended my <a href="https://research.chalmers.se/en/publication/535733">Ph.D. thesis</a>!</p>
<p><img src="https://www.mista.me/assets/img/thesis.jpg" style="width:50.0%" /></p>
<p>My days at Chalmers are numbered. I will make them count!</p>]]></summary>
</entry>
<entry>
    <title>Syntax test</title>
    <link href="https://www.mista.me/blog/test/index.html" />
    <id>https://www.mista.me/blog/test/index.html</id>
    <published>2018-06-03T00:00:00Z</published>
    <updated>2018-06-03T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>This post is mostly intended to test the Markdown syntax supported by Pandoc
with the page Css.</p>
<h2 id="plain-text">Plain Text</h2>
<p>Aliquam erat volutpat. Nunc eleifend leo vitae magna. In id erat non orci
commodo lobortis. Proin neque massa, cursus ut, gravida ut, lobortis eget,
lacus. Sed diam. Praesent fermentum tempor tellus. Nullam tempus. Mauris ac
felis vel velit tristique imperdiet. Donec at pede. Etiam vel neque nec dui
dignissim bibendum. Vivamus id enim. Phasellus neque orci, porta a, aliquet
quis, semper a, massa. Phasellus purus. Pellentesque tristique imperdiet tortor.
Nam euismod tellus id erat.</p>
<hr />
<h2 id="links">Links</h2>
<ul>
<li><a href="https://agustinmista.github.io">Here</a> is a link.</li>
<li><a href="https://agustinmista.github.io" title="Some text!">Here</a> is the same link with alt
text.</li>
</ul>
<hr />
<h2 id="images">Images</h2>
<p>Images are inserted using the following syntax. Captions are optional.</p>
<figure>
<img src="https://www.mista.me/assets/img/lambda.png" style="width:20.0%" alt="This is a caption." />
<figcaption aria-hidden="true">This is a caption.</figcaption>
</figure>
<h2 id="math">Math</h2>
<p><span class="math inline"><em>N</em><sub><em>f</em></sub></span>, the flux associated with the <span class="math inline"><em>i</em></span>th filter is <span class="math inline"><em>F</em><sub><em>i</em></sub></span>, and the weight,
<span class="math inline"><em>w</em><sub><em>i</em></sub></span>, is defined as</p>
<p><span class="math display"><em>w</em><sub><em>i</em></sub> ≡ <em>σ</em><sub><em>F</em>, <em>i</em></sub><sup>−2</sup>,</span></p>
<p>where <span class="math inline"><em>σ</em><sub><em>F</em>, <em>i</em></sub></span> is the uncertainty in the flux associated with the <span class="math inline"><em>i</em></span>th filter.
Assume that all sums range from <span class="math inline"><em>i</em> = 1</span> to <span class="math inline"><em>i</em> = <em>N</em><sub><em>f</em></sub></span> and use the summation convention
on repeated indices. Note that we strike things out like <del>this</del>, italicize things
<em>like this</em> or <em>like this</em>, and we can make things bold <strong>like this</strong> or <strong>like this</strong>.</p>
<p>Unfortunately, –&gt; and =&gt; do not turn into arrows automatically, but you can make
arrows like this <span class="math inline">→</span> and <span class="math inline">⇒</span>. You can also implement a Pandoc
<a href="http://pandoc.org/scripting.html#json-filters">filter</a> to add features that are not
built-in. So, it would be possible to make –&gt; turn into <span class="math inline">→</span> and introduce
new features, such as <a href="http://www.graphviz.org/">Graphviz</a> graph rendering.
For examples of such filters, check out
<a href="https://github.com/jgm/pandoc/wiki/Pandoc-Filters">this list</a> (or keep reading this
document).</p>
<p>Here’s a horizontal line / separator:</p>
<hr />
<h2 id="table-creation">Table Creation</h2>
<p>There are many ways of creating tables. This is an example of the pipe table
creation syntax…</p>
<table>
<colgroup>
<col style="width: 15%" />
<col style="width: 84%" />
</colgroup>
<thead>
<tr class="header">
<th style="text-align: right;">Label</th>
<th style="text-align: left;">Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;"><code>meanflx</code></td>
<td style="text-align: left;"><span class="math inline">⟨<em>F</em>⟩ = <em>F</em><sub><em>i</em></sub></span></td>
</tr>
<tr class="even">
<td style="text-align: right;"><code>wmeanflx</code></td>
<td style="text-align: left;"><span class="math inline">⟨<em>F</em>⟩<sub><em>w</em></sub> = <em>ω</em></span></td>
</tr>
</tbody>
</table>
<p>Here’s one more example, with a table caption:</p>
<table>
<caption>Demonstration of simple table syntax.</caption>
<thead>
<tr class="header">
<th style="text-align: right;">Right</th>
<th style="text-align: left;">Left</th>
<th style="text-align: center;">Center</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;">12</td>
<td style="text-align: left;">12</td>
<td style="text-align: center;">12</td>
</tr>
<tr class="even">
<td style="text-align: right;">123</td>
<td style="text-align: left;">123</td>
<td style="text-align: center;">123</td>
</tr>
<tr class="odd">
<td style="text-align: right;">1</td>
<td style="text-align: left;">1</td>
<td style="text-align: center;">1</td>
</tr>
</tbody>
</table>
<p>Refer to the <a href="http://pandoc.org/MANUAL.html#tables">documentation</a> to learn more
about the other types of tables.</p>
<hr />
<h2 id="line-blocks">Line Blocks</h2>
<p>Normally, adjacent lines are combined together, so a set of short formatted lines,
such as an address or verse, becomes jumbled. To overcome this, place <code>|</code> at the
beginning of each line, followed by a space. This preserves the formatting:</p>
<div class="line-block">I am the very model of a modern Major-General,<br />
I’ve information vegetable, animal, and mineral,<br />
I know the kings of England, and I quote the fights historical<br />
From Marathon to Waterloo, in order categorical<br />
I’m very well acquainted, too, with matters mathematical,<br />
I understand equations, both the simple and quadratical,<br />
About binomial theorem I’m teeming with a lot o’ news,<br />
With many cheerful facts about the square of the hypotenuse.<br />
<br />
I’m very good at integral and differential calculus;<br />
I know the scientific names of beings animalculous:<br />
    In short, in matters vegetable, animal, and mineral,<br />
    I am the very model of a modern Major-General.<br />
</div>
<p>Note the indentation on the final two lines above.</p>
<hr />
<h2 id="block-quotes">Block Quotes</h2>
<p>The syntax for block quotes is extremely simple:</p>
<blockquote>
<p>You can write a block quote by putting a single ‘<code>&gt;</code>’ at the beginning of a block of text
or by placing the ‘<code>&gt;</code>’ at the beginning of each line of the quoted block, similar
to the way e-mail readers handle quoted messages.</p>
</blockquote>
<hr />
<h2 id="enumerated-lists">Enumerated lists</h2>
<ol>
<li>You can use integers or the <code>#</code> symbol in enumerated lists.</li>
<li>This is quite convenient. For example,
i. You don’t have to count
<ol start="2" type="i">
<li>If you want to change the order, no numbering needs to be changed</li>
</ol></li>
<li>You can also use roman numerals, obviously.</li>
</ol>
<h2 id="example-lists">Example Lists</h2>
<ol class="example" type="1">
<li><p>This is Example (1).</p></li>
<li><p>This is Example (2).</p></li>
</ol>
<p>Now we discuss something for a while and introduce the third example…</p>
<ol start="3" class="example" type="1">
<li>This is Example (3).</li>
</ol>
<p>You can refer to an example by its label. For instance Example (2).</p>
<hr />
<h2 id="syntax-highlighting">Syntax Highlighting</h2>
<h3 id="a-python-syntax-highlighting-example">A Python syntax highlighting example:</h3>
<p>You can include blocks of pre-formatted text, like this. If the text is source code,
you can tell Pandoc to perform syntax highlighting. This is graphviz.py:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE OverloadedStrings #-}</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Monoid</span> ((&lt;&gt;))</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">System.FilePath</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Hakyll</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> hakyll <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>  match <span class="st">&quot;images/*&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>    route   idRoute</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>    compile copyFileCompiler</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>  match <span class="st">&quot;css/*&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>    route   idRoute</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>    compile compressCssCompiler</span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>  match <span class="st">&quot;templates/*&quot;</span> <span class="op">$</span> compile templateBodyCompiler</span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>  match <span class="st">&quot;index.md&quot;</span> <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a>    route   <span class="op">$</span> setExtension <span class="st">&quot;html&quot;</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a>    compile <span class="op">$</span> pandocCompiler</span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a>      <span class="op">&gt;&gt;=</span> loadAndApplyTemplate <span class="st">&quot;templates/default.html&quot;</span> defaultContext</span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a>      <span class="op">&gt;&gt;=</span> relativizeUrls</span></code></pre></div>
<h3 id="a-c-syntax-highlighting-example">A C++ syntax highlighting example:</h3>
<p>Here’s another example using a different programming language (and a different
method of specifying the pre-formatted text block).</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;iostream&gt;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// comment</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    <span class="bu">std::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;Hello, World!</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<hr />
<h2 id="compiling-this-with-pandoc">Compiling this with Pandoc</h2>
<p>To convert<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> this Markdown document to HTML, I used the following command:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co"># BTW, this is a Bash syntax highlighting example.</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> pandoc example.md <span class="at">-s</span> <span class="at">--smart</span> <span class="at">--mathjax</span> <span class="dt">\</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>         <span class="at">--css</span> nrstyle.css <span class="dt">\</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>         <span class="at">--highlight-style</span> pygments <span class="dt">\</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>         <span class="at">--columns</span><span class="op">=</span>200 <span class="dt">\</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>         <span class="at">--filter</span> graphviz.py <span class="dt">\</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>         <span class="at">-o</span> example.html</span></code></pre></div>
<h3 id="an-explanation-of-each-parameter-as-a-bulleted-list">An explanation of each parameter (as a bulleted list)</h3>
<ul>
<li><strong><code>example.md</code></strong>: the input file</li>
<li><strong><code>-s</code></strong>: create a stand-alone document (rather than a document
fragment that lacks a header).</li>
<li><strong><code>--smart</code></strong>: automatically replace <code>--</code>, <code>---</code>, and <code>...</code> with –, —, and …
and handle quotation marks properly.</li>
<li><strong><code>--mathjax</code></strong>: use the <a href="https://www.mathjax.org/">MathJax</a> library for
typesetting math in HTML documents.</li>
<li><strong><code>--css nrstyle.css</code></strong>: Use the nrstyle.css stylesheet in the HTML output document.</li>
<li><strong><code>--highlight-style pygments</code></strong>: Turn on syntax highlighting and
use the pygments color scheme.</li>
<li><strong><code>--columns=200</code></strong>: set the line length to 200. This makes the
tables display properly.</li>
<li><strong><code>--filter graphviz.py</code></strong>: Pass Pandoc’s abstract syntax tree through the filter
program <code>graphviz.py</code> before rendering the output document.</li>
<li><strong><code>-o example.html</code></strong>: Specify the name and type of the output file.
The format is inferred from the suffix (file extension).</li>
</ul>
<hr />
<h3 id="the-same-explanation-formatted-as-a-definition-list">The same explanation, formatted as a definition list</h3>
<dl>
<dt><code>example.md</code></dt>
<dd>
<p>The input file</p>
</dd>
<dt><code>-s</code></dt>
<dd>
<p>Create a stand-alone document (rather than a document fragment that lacks a header).</p>
</dd>
<dt><code>--smart</code></dt>
<dd>
<p>Automatically replace <code>--</code>, <code>---</code>, and <code>...</code> with –, —, and … and handle quotation marks properly.</p>
</dd>
<dt><code>--mathjax</code></dt>
<dd>
<p>Use the <a href="https://www.mathjax.org/">MathJax</a> library for typesetting math in HTML documents.</p>
</dd>
<dt><code>--css nrstyle.css</code></dt>
<dd>
<p>Use the nrstyle.css stylesheet in the HTML output document.</p>
</dd>
<dt><code>--highlight-style pygments</code></dt>
<dd>
<p>Turn on syntax highlighting and use the pygments color scheme.</p>
</dd>
<dt><code>--columns=200</code></dt>
<dd>
<p>set the line length to 200. This makes the tables display properly.</p>
</dd>
<dt><code>--filter graphviz.py</code></dt>
<dd>
<p>Pass Pandoc’s abstract syntax tree through the filter program <code>graphviz.py</code> before rendering the output
document.</p>
</dd>
<dt><code>-o example.html</code></dt>
<dd>
<p>Specify the name and type of the output file. The format is inferred from the suffix (file extension).</p>
</dd>
</dl>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>This is a footnote.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>Works on my machine™</title>
    <link href="https://www.mista.me/blog/works-on-my-machine/index.html" />
    <id>https://www.mista.me/blog/works-on-my-machine/index.html</id>
    <published>2022-12-12T00:00:00Z</published>
    <updated>2022-12-12T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>I’ve been having issues compiling LaTeX projects that use Lhs2TeX+pdfTeX+BibTeX in a reproductible manner between my workstation and GitHub’s CI, and while there are a bunch of solutions for building LaTeX in CI and VSCode without much hassle, I couldn’t quite find the way to make them work with Lhs2Tex in the pipeline. To solve this, I decided to homogenize the build environment using a Docker image, the same one everywhere. This works pretty well but turned out to be a bit nuanced, so these are the instructions for future me.</p>
<p>The setup consists of three parts:</p>
<ol type="1">
<li>A <a href="https://hub.docker.com/r/agustinmista/latex-lhs2tex">Docker image</a> with <code>texlive-full</code> and <code>lhs2tex</code> preinstalled. It gets built and deployed automatically to DockerHub using a <a href="https://github.com/agustinmista/latex-lhs2tex-docker/blob/main/.github/workflows/docker.yml">GitHub action</a>.</li>
<li>A GitHub action inside my project that pulls the Docker image (1) and uses it to build the LaTeX documents (via <code>make</code>), exporting all the generated PDFs as an artifact.</li>
<li>A couple of config files to instruct VSCode to open my project inside the same Docker image (1) and rebuild the documents after saving a modified source file.</li>
</ol>
<p>Let’s take a deeper look at each part now.</p>
<h2 id="build-environment">Build environment</h2>
<p>The LaTeX pipeline I use is relatively simple. First, we preprocesses the root <code>.lhs.tex</code> files of my project using <code>lhs2tex</code>. This produces some gigantic <code>.tex</code> files that we then compile with the usual <code>pdflatex</code>+<code>bibtex</code>+<code>pdflatex</code>+<code>pdflatex</code> nonsense. These two steps are defined using a simple Makefile. I wrote <a href="https://github.com/agustinmista/latex-lhs2tex-docker/blob/main/Dockerfile">this Dockerfile</a> to automate the process of installing the dependencies needed to run the pipeline.</p>
<p>For simplicity, I use a GitHub action to build and <a href="https://hub.docker.com/r/agustinmista/latex-lhs2tex">deploy this image to DockerHub</a> remotely on every new push. This is quite straightforward to set up using some existing actions:</p>
<p><strong>.github/workflows/docker.yml</strong>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">name</span><span class="kw">:</span><span class="at"> Docker Image CI</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="fu">env</span><span class="kw">:</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">IMAGE_NAME</span><span class="kw">:</span><span class="at"> latex-lhs2tex</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="fu">on</span><span class="kw">:</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">push</span><span class="kw">:</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">branches</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="at"> main </span><span class="kw">]</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="fu">jobs</span><span class="kw">:</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">build</span><span class="kw">:</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="kw">-</span><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v3</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Login to Docker Hub</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> docker/login-action@v1</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">username</span><span class="kw">:</span><span class="at"> ${{ secrets.DOCKERHUB_USER }}</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">password</span><span class="kw">:</span><span class="at"> ${{ secrets.DOCKERHUB_TOKEN }}</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Set up Docker Buildx</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> docker/setup-buildx-action@v1</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">driver</span><span class="kw">:</span><span class="at"> docker-container</span></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a><span class="fu">        driver-opts</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a>          image=moby/buildkit:master</span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a>          network=host</span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Build and push</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> docker/build-push-action@v2</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">context</span><span class="kw">:</span><span class="at"> ./</span></span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">file</span><span class="kw">:</span><span class="at"> ./Dockerfile</span></span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">builder</span><span class="kw">:</span><span class="at"> ${{ steps.buildx.outputs.name }}</span></span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">push</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">tags</span><span class="kw">:</span><span class="at"> ${{ secrets.DOCKERHUB_USER }}/${{ env.IMAGE_NAME }}:latest</span></span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">cache-from</span><span class="kw">:</span><span class="at"> type=registry,ref=${{ secrets.DOCKERHUB_USER }}/${{ env.IMAGE_NAME }}:buildcache</span></span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">cache-to</span><span class="kw">:</span><span class="at"> type=registry,ref=${{ secrets.DOCKERHUB_USER }}/${{ env.IMAGE_NAME }}:buildcache,mode=max</span></span></code></pre></div>
<p>These definitions live in a <a href="https://github.com/agustinmista/latex-lhs2tex-docker">separate repository</a> so I can reuse it for different LaTeX projects. The <code>secrets.DOCKERHUB_USER</code> and <code>secrets.DOCKERHUB_TOKEN</code> are the credentials used to login into DockerHub and need to be set using the web UI. Finally, in the build step it’s important to setup the <code>cache-from</code> and <code>cache-to</code> so we can additionally cache the intermediate image layers in DockerHub, and our future commits don’t take an eternity to rebuild.</p>
<h2 id="github-ci-action">GitHub CI action</h2>
<p>With the build environment already deployed to DockerHub, the next step is to create a CI action in the project’s repo that pulls the image and runs <code>make</code> inside the build environment:</p>
<p><strong>.github/workflows/latex.yml</strong>:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">name</span><span class="kw">:</span><span class="at"> Build LaTeX documents</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="fu">env</span><span class="kw">:</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">BUILD_ENV_IMAGE</span><span class="kw">:</span><span class="at"> agustinmista/latex-lhs2tex</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="fu">on</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="at">push</span><span class="kw">]</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="fu">jobs</span><span class="kw">:</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">build</span><span class="kw">:</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Set up Git repository</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v3</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="at">          </span><span class="fu">path</span><span class="kw">:</span><span class="at"> repo</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Fix file permissions in repo</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="fu">        run</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>          chmod -R 777 repo</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>          sudo chown -R 1000:1000 repo</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Pull custom Docker image</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">run</span><span class="kw">:</span><span class="at"> docker pull ${{ env.BUILD_ENV_IMAGE }}</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Compile LaTeX documents using custom Docker image</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a><span class="fu">        run</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a>          docker run \</span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a>            -v ${{ github.workspace }}/repo:/home/docker/workdir \</span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a>            ${{ env.BUILD_ENV_IMAGE }} \</span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a>            -c &quot;make all&quot;</span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Upload PDF file</span></span>
<span id="cb2-33"><a href="#cb2-33" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/upload-artifact@v3</span></span>
<span id="cb2-34"><a href="#cb2-34" aria-hidden="true" tabindex="-1"></a><span class="at">        </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb2-35"><a href="#cb2-35" aria-hidden="true" tabindex="-1"></a><span class="at">          </span><span class="fu">name</span><span class="kw">:</span><span class="at"> artifact</span></span>
<span id="cb2-36"><a href="#cb2-36" aria-hidden="true" tabindex="-1"></a><span class="at">          </span><span class="fu">path</span><span class="kw">:</span><span class="at"> repo/*.pdf</span></span></code></pre></div>
<p>A little nuance here is that I’m building the build enviroment image using the usual <code>uid:gid=1000:1000</code> so build containers can modify and create files in the host files without screwing up their permissions. To my surprise, GitHub does <strong>not</strong> follow this, and the action <code>runner</code> user has instead <code>uid:gid=1001:121</code>. I’ve seen that some projects solve this by building Docker images to be used by GitHub runners using those specific <code>uid:gid</code> values, but this approach would probably not work if we want to also use the image later to build the project locally. The easiest way to solve it is to <code>chown</code>+<code>chmod</code> the cloned project folder <code>repo</code> so they belong to the build container user, but everyone can access them (this is needed by the last step to find and export the generated PDFs as an artifact).</p>
<h2 id="vscode-setup">VSCode setup</h2>
<p>Now it’s time to bring the same build environment we used in CI to my local machine, so I don’t have to worry anymore about reproductibility. The easiest way to do it in VSCode is by using a <a href="https://code.visualstudio.com/docs/devcontainers/containers">development container</a>. We do this by creating the following file in our project’s repository:</p>
<p><strong>.devcontainer/devcontainer.json</strong>:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;image&quot;</span><span class="fu">:</span> <span class="st">&quot;agustinmista/latex-lhs2tex&quot;</span><span class="fu">,</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;customizations&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;vscode&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>      <span class="dt">&quot;extensions&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>        <span class="st">&quot;mathematic.vscode-pdf&quot;</span><span class="ot">,</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>        <span class="st">&quot;Gruntfuggly.triggertaskonsave&quot;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>      <span class="ot">]</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>    <span class="fu">}</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>  <span class="fu">}</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p>The important bit here is that we’re telling VSCode that this project is meant to be developed inside the <a href="https://hub.docker.com/r/agustinmista/latex-lhs2tex">agustinmista/latex-lhs2tex</a> Docker image.</p>
<p>Additionally, we can customize VSCode with specific extensions to be enabled for this particular project. Here I’m including the “Trigger Task on Save” extension that let us run VSCode tasks when we save specific files. These tasks are specified under <code>.vscode/tasks.json</code>. In our case, we can define a <code>make</code> task that simply runs <code>make all</code>:</p>
<p><strong>.vscode/tasks.json</strong>:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;version&quot;</span><span class="fu">:</span> <span class="st">&quot;2.0.0&quot;</span><span class="fu">,</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;tasks&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">{</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>      <span class="dt">&quot;label&quot;</span><span class="fu">:</span> <span class="st">&quot;make&quot;</span><span class="fu">,</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>      <span class="dt">&quot;type&quot;</span><span class="fu">:</span> <span class="st">&quot;shell&quot;</span><span class="fu">,</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>      <span class="dt">&quot;command&quot;</span><span class="fu">:</span> <span class="st">&quot;make all&quot;</span><span class="fu">,</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>      <span class="dt">&quot;problemMatcher&quot;</span><span class="fu">:</span> <span class="ot">[]</span><span class="fu">,</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>      <span class="dt">&quot;group&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&quot;kind&quot;</span><span class="fu">:</span> <span class="st">&quot;build&quot;</span><span class="fu">,</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&quot;isDefault&quot;</span><span class="fu">:</span> <span class="kw">true</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>      <span class="fu">}</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>    <span class="fu">}</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>  <span class="ot">]</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p>To make this work, we now need to configure the extension to run the <code>make</code> task when we save some modified source file. This can be done by adding the following to the repo’s local <code>.vscode/settings.json</code>:</p>
<p><strong>.vscode/settings.json</strong>:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;triggerTaskOnSave.tasks&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&quot;make&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;*.lhs.tex&quot;</span><span class="ot">,</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;notation.fmt&quot;</span><span class="ot">,</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;references.bib&quot;</span><span class="ot">,</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>      <span class="st">&quot;path/to/other/source/files/*.tex&quot;</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    <span class="ot">]</span><span class="fu">,</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>  <span class="fu">},</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;triggerTaskOnSave.showNotifications&quot;</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;triggerTaskOnSave.on&quot;</span><span class="fu">:</span> <span class="kw">true</span><span class="fu">,</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&quot;triggerTaskOnSave.restart&quot;</span><span class="fu">:</span> <span class="kw">true</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p>And that’s it. Next time we open the project, VSCode should ask us if we want to reopen it inside our build environment. Click “Yes” and carry on looking for those <code>missing $ inserted</code>.</p>]]></summary>
</entry>

</feed>
