<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Larvitz Blog - Cooking</title><link href="https://blog.hofstede.it/" rel="alternate"/><link href="https://blog.hofstede.it/feeds/cooking.atom.xml" rel="self"/><id>https://blog.hofstede.it/</id><updated>2026-07-07T00:00:00+02:00</updated><subtitle>FreeBSD, Linux, all things cleanly engineered</subtitle><entry><title>chana_masala.sh: A Recipe as a Shell Script</title><link href="https://blog.hofstede.it/chana_masalash-a-recipe-as-a-shell-script/" rel="alternate"/><published>2026-07-07T00:00:00+02:00</published><updated>2026-07-07T00:00:00+02:00</updated><author><name>Larvitz</name></author><id>tag:blog.hofstede.it,2026-07-07:/chana_masalash-a-recipe-as-a-shell-script/</id><summary type="html">&lt;p&gt;Some years ago a chana masala arrived in a HelloFresh box and never left the household again. It has since been refactored, hardened, and put under version control. Here is the current release: a chickpea curry for hungry &lt;span class="caps"&gt;IT&lt;/span&gt; people, documented the only way I know how - as a bash script with a proper runbook&amp;nbsp;attached.&lt;/p&gt;</summary><content type="html">&lt;hr&gt;
&lt;h2 id="table-of-contents"&gt;Table of&amp;nbsp;Contents&lt;/h2&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#table-of-contents"&gt;Table of&amp;nbsp;Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-source"&gt;The&amp;nbsp;Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-runbook"&gt;The Runbook&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#dependencies"&gt;Dependencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#prepare_paste"&gt;prepare_paste&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#build_masala"&gt;build_masala&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#add_chickpeas"&gt;add_chickpeas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#serve"&gt;serve&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#post-incident-notes"&gt;Post-Incident&amp;nbsp;Notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;Every household has one dish that started as an accident and ended up in production. Ours arrived some years ago in a HelloFresh box - yes, the meal-kit subscription, our guilty pleasure phase - as an unassuming chickpea curry. We cooked it once, looked at each other, and knew this thing was not going back into the backlog. The subscription is long gone; the recipe survived the migration. It has been running in our kitchen on a roughly weekly release cadence ever since, patched and tuned along the way like any long-lived service: the spice list grew, the onion-garlic-ginger paste became a hard dependency, and whole spices got promoted from experiment to&amp;nbsp;optional-but-recommended.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Chana masala" src="https://blog.hofstede.it/images/2026-07-07-chana-masala-nerd-edition.jpg" title="Chana masala, fresh from the release pipeline"&gt;&lt;/p&gt;
&lt;p&gt;At some point I did what I do with everything that runs regularly and must not fail: I wrote it down as a script. Not as a joke, or at least not only as a joke. The shell script format turns out to be a genuinely good fit for recipes. It forces you to declare your dependencies up front, it makes the sequencing explicit,&amp;nbsp;and &lt;code&gt;set -euo pipefail&lt;/code&gt; is exactly the attitude you want at the stove: if a step fails, stop and look at it, do not keep stirring and&amp;nbsp;hope.&lt;/p&gt;
&lt;p&gt;So here it is. First the source code, then the runbook for humans who would rather not parse bash while&amp;nbsp;hungry.&lt;/p&gt;
&lt;h2 id="the-source"&gt;The&amp;nbsp;Source&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-euo&lt;span class="w"&gt; &lt;/span&gt;pipefail
&lt;span class="c1"&gt;# ============================================================&lt;/span&gt;
&lt;span class="c1"&gt;# chana_masala.sh&lt;/span&gt;
&lt;span class="c1"&gt;# Version: 1.0.0&lt;/span&gt;
&lt;span class="c1"&gt;# Description: Chickpea masala for hungry IT nerds&lt;/span&gt;
&lt;span class="c1"&gt;# Runtime: approx. 25-30 minutes&lt;/span&gt;
&lt;span class="c1"&gt;# Dependencies: pan, stove, appetite&lt;/span&gt;
&lt;span class="c1"&gt;# ============================================================&lt;/span&gt;

&lt;span class="nv"&gt;PAN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/dev/pan&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;STOVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/sys/kitchen/stove&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;PLATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/home/user/plate&amp;quot;&lt;/span&gt;

&lt;span class="nv"&gt;OIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;oil or ghee&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;CHICKPEAS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pre-cooked chickpeas&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;TOMATOES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fresh tomatoes or a can of chopped tomatoes&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;BASE_PASTE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;onion-garlic-ginger paste&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;TOPPING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cilantro || parsley || true&amp;quot;&lt;/span&gt;

&lt;span class="nv"&gt;SPICES&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ground cumin&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ground coriander&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;paprika&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;chili powder&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;garam masala&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;turmeric&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;salt&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pepper&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bay leaf&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;OPTIONAL_WHOLE_SPICES&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;whole cumin seeds&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cinnamon stick&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;green cardamom pods&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;coriander seeds&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;prepare_paste&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[1/4] Initializing aroma base...&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;dice&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$TOMATOES&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/tomatoes.cubes

&lt;span class="w"&gt;    &lt;/span&gt;peel&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;onions&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;garlic&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ginger&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;foodprocessor&lt;span class="w"&gt; &lt;/span&gt;--mode&lt;span class="o"&gt;=&lt;/span&gt;paste&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/ogg_paste.masala

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Fallback for systems without a food processor:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# knife --fine-chop onions garlic ginger &amp;gt; /tmp/ogg_paste.masala&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Onion-garlic-ginger paste built successfully.&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;build_masala&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[2/4] Starting masala engine...&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;heat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--level&lt;span class="o"&gt;=&lt;/span&gt;high
&lt;span class="w"&gt;    &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$OIL&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;spice&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OPTIONAL_WHOLE_SPICES&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$spice&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;sleep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# seconds: launch the aroma bootloader&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/tmp/ogg_paste.masala&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;heat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--level&lt;span class="o"&gt;=&lt;/span&gt;medium
&lt;span class="w"&gt;    &lt;/span&gt;sleep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;180&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# sweat gently, do not let it commit (burn)&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;spice&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SPICES&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$spice&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;sleep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;35&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# compile spices and release aroma&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/tmp/tomatoes.cubes&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;fry&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--mode&lt;span class="o"&gt;=&lt;/span&gt;sharp
&lt;span class="w"&gt;    &lt;/span&gt;simmer&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--until&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tomatoes soft&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;water&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;simmer&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--mode&lt;span class="o"&gt;=&lt;/span&gt;sauce
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;add_chickpeas&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[3/4] Deploying chickpeas...&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$CHICKPEAS&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sauce&lt;span class="w"&gt; &lt;/span&gt;--is-too-thick&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;a splash of water&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;taste&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;adjust&lt;span class="w"&gt; &lt;/span&gt;--with&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;salt,pepper&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;lid&lt;span class="w"&gt; &lt;/span&gt;--close&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;heat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--level&lt;span class="o"&gt;=&lt;/span&gt;low
&lt;span class="w"&gt;    &lt;/span&gt;sleep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# 5 minutes gentle simmer&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;serve&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[4/4] Plating the release candidate...&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PAN&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PLATE&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;fresh cilantro&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PLATE&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sesame seeds&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PLATE&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;garlic topping&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PLATE&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;serve_with&lt;span class="w"&gt; &lt;/span&gt;--any-of&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;basmati rice&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;roti&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;chapati&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;naan&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;sidecar&lt;span class="w"&gt; &lt;/span&gt;--optional&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;yogurt-mint dip&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Status: 200 OK - dinner served successfully.&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;require&lt;span class="w"&gt; &lt;/span&gt;appetite
&lt;span class="w"&gt;    &lt;/span&gt;require&lt;span class="w"&gt; &lt;/span&gt;pan
&lt;span class="w"&gt;    &lt;/span&gt;require&lt;span class="w"&gt; &lt;/span&gt;stove

&lt;span class="w"&gt;    &lt;/span&gt;prepare_paste
&lt;span class="w"&gt;    &lt;/span&gt;build_masala
&lt;span class="w"&gt;    &lt;/span&gt;add_chickpeas
&lt;span class="w"&gt;    &lt;/span&gt;serve

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Done. Do not deploy to production without seconds.&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

main&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="the-runbook"&gt;The&amp;nbsp;Runbook&lt;/h2&gt;
&lt;p&gt;The script is complete, but no on-call engineer should have to reverse-engineer dinner from bash. Here is the same procedure in prose, with the default values we converged on after years of operation. Serves two to three, depending on appetite and whether anyone honors the seconds warning at the&amp;nbsp;end.&lt;/p&gt;
&lt;h3 id="dependencies"&gt;Dependencies&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;2 cans of chickpeas (about 480 g drained), or the equivalent pre-cooked from&amp;nbsp;dry&lt;/li&gt;
&lt;li&gt;400 g chopped tomatoes, fresh or from a can - the can is fine, this is a weeknight service, not a tasting&amp;nbsp;menu&lt;/li&gt;
&lt;li&gt;2 onions, 4 cloves of garlic, a thumb-sized piece of&amp;nbsp;ginger&lt;/li&gt;
&lt;li&gt;2 tbsp oil or&amp;nbsp;ghee&lt;/li&gt;
&lt;li&gt;Ground spices: 1 tsp cumin, 1 tsp coriander, 1 tsp paprika, ½ tsp chili powder (tune to your threat model), 1 tsp garam masala, ½ tsp turmeric, 1 bay leaf, salt and&amp;nbsp;pepper&lt;/li&gt;
&lt;li&gt;Optional whole spices: ½ tsp cumin seeds, 1 cinnamon stick, 2-3 green cardamom pods, ½ tsp coriander&amp;nbsp;seeds&lt;/li&gt;
&lt;li&gt;Topping: fresh cilantro (or parsley, if cilantro tastes like soap on your hardware - that is a genetic compile flag, not a character flaw), sesame seeds, crispy&amp;nbsp;garlic&lt;/li&gt;
&lt;li&gt;Sidecar: basmati rice, roti, chapati, or naan; optionally a yogurt-mint&amp;nbsp;dip&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="prepare_paste"&gt;prepare_paste&lt;/h3&gt;
&lt;p&gt;Dice the tomatoes and set them aside. Peel the onions, garlic, and ginger and run them through a food processor until they form a rough paste. No food processor? The documented fallback applies: chop everything as finely as your knife skills allow. The paste is the load-bearing component of this dish; do not skip it in favor of lazily sliced onions. I have tried. The diff is&amp;nbsp;noticeable.&lt;/p&gt;
&lt;h3 id="build_masala"&gt;build_masala&lt;/h3&gt;
&lt;p&gt;Heat the pan on high and add the oil or ghee. If you are using the whole spices, they go in first, alone, for about fifteen seconds - this is the aroma bootloader, and you will smell exactly when it has finished initializing. Then add the onion-garlic-ginger paste, drop the heat to medium, and let it sweat for a good three minutes. The goal is soft and fragrant, not brown. Watch it. This is the step where the dish burns if you go check on something in another&amp;nbsp;terminal.&lt;/p&gt;
&lt;p&gt;Add all the ground spices and stir them through the paste for about half a minute so they toast and bloom in the fat. Then add the tomatoes, fry everything sharply for a moment, and simmer until the tomatoes have collapsed into the sauce. Add a splash of water and let it reduce back to a thick, glossy&amp;nbsp;masala.&lt;/p&gt;
&lt;h3 id="add_chickpeas"&gt;add_chickpeas&lt;/h3&gt;
&lt;p&gt;Drain the chickpeas and stir them in. If the sauce is too thick, loosen it with a little water; if it is too thin, let it simmer uncovered a bit longer. Taste, adjust salt and pepper, then put the lid on, drop the heat to low, and give it five minutes so the chickpeas take on the sauce. This idle loop is not optional. It is the difference between chickpeas in sauce and chana&amp;nbsp;masala.&lt;/p&gt;
&lt;h3 id="serve"&gt;serve&lt;/h3&gt;
&lt;p&gt;Plate over basmati rice or with fresh roti, chapati, or naan. Top with fresh cilantro, sesame seeds, and crispy garlic. A yogurt-mint dip on the side handles any chili overprovisioning&amp;nbsp;gracefully.&lt;/p&gt;
&lt;p&gt;Status: 200 &lt;span class="caps"&gt;OK&lt;/span&gt;.&lt;/p&gt;
&lt;h2 id="post-incident-notes"&gt;Post-Incident&amp;nbsp;Notes&lt;/h2&gt;
&lt;p&gt;A few operational learnings from years of running this in&amp;nbsp;production:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The whole spices are marked optional in the script but I no longer build without them.&lt;/strong&gt; The fifteen seconds of cumin seeds and cardamom in hot ghee changes the whole flavor baseline. Consider them optional the way monitoring is&amp;nbsp;optional.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It reheats better than it serves fresh.&lt;/strong&gt; Like most curries, the flavor keeps integrating overnight. Cook double, and tomorrow&amp;#8217;s lunch is a zero-effort deployment from the&amp;nbsp;fridge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The chili powder is the only genuinely environment-specific value.&lt;/strong&gt; Everything else in the spice array has been stable for years; the chili gets overridden per&amp;nbsp;audience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HelloFresh deserves the credit for the initial commit.&lt;/strong&gt; We forked the recipe, rewrote most of it, and stopped paying the subscription fee, which I suppose makes this the classic open-core story told from the other side. But the upstream project shipped a genuinely good v0.1, and this post is the attribution&amp;nbsp;notice.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do not deploy to production without&amp;nbsp;seconds.&lt;/p&gt;</content><category term="Cooking"/><category term="cooking"/><category term="recipe"/><category term="bash"/><category term="off-topic"/></entry></feed>