<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Richard Brunt - Today I Learned</title>
  <subtitle>Sometimes I learn something and want to jot it down for future reference. This is where I can do that. Hopefully it&#39;s helpful for others too.</subtitle>
  <link href="https://richardbrunt.co.uk/tils.xml" rel="self" />
  <link href="https://richardbrunt.co.uk/til/" />
  <updated>2025-10-14T00:00:00Z</updated>
  <id>https://richardbrunt.co.uk/til/</id>
  <author>
    <name>Richard Brunt</name>
  </author>
  <entry>
    <title>Dockerized Lambda deployments with CloudFormation</title>
    <link href="https://richardbrunt.co.uk/til/dockerized-lambda-deployments-with-cloudformation/" />
    <updated>2025-10-14T00:00:00Z</updated>
    <id>https://richardbrunt.co.uk/til/dockerized-lambda-deployments-with-cloudformation/</id>
    <content type="html">&lt;p&gt;When you deploy a Lambda using a tagged docker image, updating the tagged image and redeploying the Lambda (using CloudFormation) doesn&#39;t actually update the code running &lt;em&gt;in&lt;/em&gt; the Lambda. This happens because neither CloudFormation nor Lambda know that the tag now points somewhere else.&lt;/p&gt;
&lt;p&gt;The solution is actually pretty simple: instead of referring to the docker image by its &lt;em&gt;tag&lt;/em&gt;, refer to it by its &lt;a href=&quot;https://docs.docker.com/dhi/core-concepts/digests/&quot;&gt;digest&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;Old way, using a tag:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Part of a CloudFormation template in yaml format:&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;LambdaFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AWS&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Lambda&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Function
  &lt;span class=&quot;token key atrule&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;PackageType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Image
      &lt;span class=&quot;token key atrule&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;120&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;MemorySize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;ImageUri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;!Sub&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;DockerRegistry&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/image&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;name@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;New way, using a digest:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Part of a CloudFormation template in yaml format:&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;LambdaFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; AWS&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Lambda&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;Function
  &lt;span class=&quot;token key atrule&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;PackageType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Image
      &lt;span class=&quot;token key atrule&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;120&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;MemorySize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;512&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Reference the digest (sha hash) instead of tag here, since Lambda won&#39;t pick up the new image if we just re-tag it.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# Doing it this way ensure lambda pulls the new image.&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;ImageUri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;!Sub&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;DockerRegistry&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/image&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;name@$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;LambdaDockerDigest&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The digest comes into the template as a parameter (&lt;code&gt;LambdaDockerDigest&lt;/code&gt;), which ultimately comes from my build step and looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sha256:94a00394bc5a8ef503fb59db0a7d0ae9e1110866e8aee8ba40cd864cea69ea1a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In hindsight, using the sha is probably better anyway: it&#39;s the most unambiguous and immutable way of referring to a given image version.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Installing Docker Buildx on Windows without Docker Desktop</title>
    <link href="https://richardbrunt.co.uk/til/installing-docker-buildx-on-windows-without-docker-desktop/" />
    <updated>2025-07-24T00:00:00Z</updated>
    <id>https://richardbrunt.co.uk/til/installing-docker-buildx-on-windows-without-docker-desktop/</id>
    <content type="html">&lt;p&gt;If you don&#39;t install Docker Desktop for windows but just install the cli tool manually (e.g. because your company &lt;a href=&quot;https://www.servethehome.com/docker-abruptly-starts-charging-many-users-for-docker-desktop/&quot;&gt;doesn&#39;t want to pay for it&lt;/a&gt;), it&#39;s not obvious how you get plugins like &lt;code&gt;buildx&lt;/code&gt; installed.&lt;/p&gt;
&lt;p&gt;It turns out that it&#39;s not actually that hard, just a bit manual:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download the latest Windows executable from the &lt;a href=&quot;https://github.com/docker/buildx/releases/latest&quot;&gt;Github Release page&lt;/a&gt;. Look for the file the ends with &lt;code&gt;windows-amd64.exe&lt;/code&gt; (e.g. &lt;code&gt;buildx-v0.26.1.windows-amd64.exe&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Rename the downloaded file to &lt;code&gt;docker-buildx.exe&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy the renamed file to &lt;code&gt;%USERPROFILE%&#92;.docker&#92;cli-plugins&lt;/code&gt;, creating the directories if needed.&lt;/li&gt;
&lt;li&gt;Verify that it&#39;s working by running &lt;code&gt;docker buildx --help&lt;/code&gt; in your Windows Terminal.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I haven&#39;t tested this for other plugins like &lt;code&gt;docker compose&lt;/code&gt;, but I expect the procedure would be the same.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Setuptools package data setting for config files</title>
    <link href="https://richardbrunt.co.uk/til/setuptools-package-data-setting-for-config-files/" />
    <updated>2025-06-06T00:00:00Z</updated>
    <id>https://richardbrunt.co.uk/til/setuptools-package-data-setting-for-config-files/</id>
    <content type="html">&lt;p&gt;Under certain circumstances, &lt;a href=&quot;https://setuptools.pypa.io&quot;&gt;setuptools&lt;/a&gt; won&#39;t copy static files like config files into the built package &lt;a href=&quot;https://setuptools.pypa.io/en/latest/userguide/datafiles.html&quot;&gt;unless you tell it to in the build config&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# pyproject.toml&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# further up, the default value of &#39;include-package-data = true&#39; had been set to false.&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;tools.setuptools.package-data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;my_package_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;config/**/*.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pretty_picture.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tripped me up because when I was building a python program for deployment in Docker, things worked fine (it was installed &#39;editable&#39; so no copying of files), but when I bundled everything up for deployment in a lambda using &lt;code&gt;pip install . --target dist&lt;/code&gt;, the config wasn&#39;t there!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>pytest: start a debugger on test failure</title>
    <link href="https://richardbrunt.co.uk/til/pytest-start-a-debugger-on-test-failure/" />
    <updated>2025-05-28T00:00:00Z</updated>
    <id>https://richardbrunt.co.uk/til/pytest-start-a-debugger-on-test-failure/</id>
    <content type="html">&lt;p&gt;While refactoring some tests, I stumbled upon a really cool pytest feature I wish I had known about before: pytest has a &lt;a href=&quot;https://docs.pytest.org/en/stable/how-to/failures.html#using-python-library-pdb-with-pytest&quot;&gt;command line argument (&lt;code&gt;--pdb&lt;/code&gt;)&lt;/a&gt; that drops you into a &lt;a href=&quot;https://docs.python.org/3/library/pdb.html#module-pdb&quot;&gt;pdb&lt;/a&gt; debug shell whenever a test fails. Really handy for inspecting the state of the test to figure out why it&#39;s failing or why your assertion isn&#39;t working as expected!&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;pytest &lt;span class=&quot;token parameter variable&quot;&gt;--pdb&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# drops to debug shell on each test failure&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are more examples in the &lt;a href=&quot;https://richardbrunt.co.uk/til/pytest-start-a-debugger-on-test-failure/&quot;&gt;pytest docs&lt;/a&gt;, including &lt;code&gt;--trace&lt;/code&gt; which drops you into a debug shell at the &lt;em&gt;start&lt;/em&gt; of each test so you can step through it line by line.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Custom version format for Mend Renovate</title>
    <link href="https://richardbrunt.co.uk/til/custom-version-format-for-mend-renovate/" />
    <updated>2025-05-26T00:00:00Z</updated>
    <id>https://richardbrunt.co.uk/til/custom-version-format-for-mend-renovate/</id>
    <content type="html">&lt;p&gt;I use &lt;a href=&quot;https://docs.mend.io/wsk/mend-renovate&quot;&gt;Mend Renovate&lt;/a&gt; to keep docker images of applications I run up-to-date.&lt;/p&gt;
&lt;p&gt;Today I noticed that my &lt;a href=&quot;https://umami.is/&quot;&gt;umami analytics&lt;/a&gt; dashboard said it had an update available, but renovate hadn&#39;t told me about it.
On closer inspection it was because the docker tag format umami use is slightly unusual: they have a prefix with the database driver to use, followed by the version number (e.g. &lt;code&gt;postgresql-v1.17.0&lt;/code&gt; or &lt;code&gt;mysql-v1.17.0&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Renovate didn&#39;t understand how to parse this format, so I had to add a custom package rule in my &lt;code&gt;renovate.json&lt;/code&gt; to tell it how to interpret them:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;$schema&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://docs.renovatebot.com/renovate-schema.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Rest of config here&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;packageRules&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;matchDatasources&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;docker&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;matchPackageNames&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ghcr.io/umami-software/umami&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;versioning&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;regex:^postgresql-v(?&amp;lt;major&gt;&#92;&#92;d+)&#92;&#92;.(?&amp;lt;minor&gt;&#92;&#92;d+)&#92;&#92;.(?&amp;lt;patch&gt;&#92;&#92;d+)$&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here I&#39;ve fixed it to postgres only, as otherwise renovate just picks between postgres and mysql at random, which isn&#39;t going to work well!&lt;/p&gt;
</content>
  </entry>
</feed>