This is a valid RSS feed.
This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.
line 174, column 260: (8 occurrences) [help]
... o that's 3x speed improvement.</p></description><pubDate>Mon, 16 F ...
^
<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Peterbe.com</title><link>http://www.peterbe.com/rss.xml</link><description>Stuff in Peter's head</description><atom:link href="http://www.peterbe.com/rss.xml" rel="self"/><language>en-us</language><lastBuildDate>Wed, 29 Apr 2026 12:22:01 +0000</lastBuildDate><item><title>Bun WebView is eating up my tmp storage</title><link>http://www.peterbe.com/plog/bun-webview-is-eating-up-my-tmp-storage</link><description>Bun WebView API creates tmp files that are large. It can eat your disk</description><pubDate>Wed, 29 Apr 2026 12:22:01 +0000</pubDate><guid>http://www.peterbe.com/plog/bun-webview-is-eating-up-my-tmp-storage</guid></item><item><title>html-getter - A powerfully simple HTML scraper in Bun</title><link>http://www.peterbe.com/plog/html-getter</link><description><p>When <a href="https://bun.com/blog/bun-v1.3.12">Bun v1.3.12</a> was released last week, they subtly included a powerful feature, almost under the radar: <code>WebView</code>. It's like Playwright/Puppeteer but more limited, especially in terms of documentation.</p><p>I built a demo app based on it called <a href="https://github.com/peterbe/html-getter">html-getter</a> which you can use like this:</p><pre><code class="hljs">bun run src/html-getter.ts https://www.peterbe.com</code></pre> <p>That will open <code>https://www.peterbe.com</code> with Chrome or Chromium or WebKit and evaluate the following JavaScript expression: <code>document.documentElement.outerHTML</code> (which is the whole HTML source of the whole DOM) and print this to stdout.</p><p>or you can use it like this:</p><pre><code class="hljs">bun run src/html-getter.ts --body --screenshot /tmp/page.png https://www.peterbe.com</code></pre> <p>which means it prints everything inside the <code>&lt;body&gt;...&lt;/body&gt;</code> instead and at the same time, it dumps a screenshot of what it looks like, which can be useful for debugging.</p><p>What's cool about this is that you no longer need to install <code>playwright</code> or <code>puppeteer</code>. Scraping HTML is just one of many applications you can use <code>WebView</code> for. I wouldn't use it for automated headless browser testing though because the documentation, tooling, and APIs for Playwright is still so much much better.</p><p><strong>WARNING</strong></p><p>At the time of writing this, I am unable, on macOS, to compile this Bun script into a binary. When I do, this happens:</p><pre><code class="hljs">$ bun run build$ bun build src/html-getter.ts --target=bun --outfile out/html-getter --compile --bytecode [14ms] bundle 1 modules [77ms] compile out/html-getter$ ./out/html-getter https://www.peterbe.com[1] 70304 killed ./out/html-getter https://www.peterbe.com</code></pre> <p>The same worked on my x64 Linux server, but to get this to compile on macOS might need some more careful thought.</p></description><pubDate>Fri, 17 Apr 2026 13:34:17 +0000</pubDate><guid>http://www.peterbe.com/plog/html-getter</guid></item><item><title>Bestest security tip for updating packages with Bun</title><link>http://www.peterbe.com/plog/bestest-security-tip-for-updating-packages-with-bun</link><description><p>I don't know when this was added but if you use Bun in your TypeScript project, you might be familiar with <code>bun upgrade</code> which is a CLI tool for upgrading the packages you pin and depend on. You can now pass it a "cool down period" which means a certain package update doesn't count unless it's been published for at least X hours.</p><p><strong>This is critical for avoiding installing compromised NPM packages.</strong> Sometimes a package gets hacked. If you were to be unlucky and upgrade to it at that window of time, you can have pinned an insecure/compromised version into your lock file. When the <a href="https://www.trendmicro.com/en_us/research/26/c/axios-npm-package-compromised.html">Axios supply chain attack</a> happened, there was a 39 minute window when installing the latest version of <code>axios</code> would (potentially) sneak in the bad package version.</p><p>Now, this is how I use <code>bun update</code>:</p><pre><code class="hljs">bun update --interactive --minimum-release-age=86400</code></pre> <p>That means, it won't upgrade anything that hasn't sat for at least 1 day.</p></description><pubDate>Tue, 14 Apr 2026 14:25:40 +0000</pubDate><guid>http://www.peterbe.com/plog/bestest-security-tip-for-updating-packages-with-bun</guid></item><item><title>pytest "import file mismatch"</title><link>http://www.peterbe.com/plog/pytest-import-file-mismatch</link><description>Make sure your test files in a pytest tested project sit in directories with a __init__.py</description><pubDate>Wed, 01 Apr 2026 13:20:53 +0000</pubDate><guid>http://www.peterbe.com/plog/pytest-import-file-mismatch</guid></item><item><title>Copy the current line in VS Code</title><link>http://www.peterbe.com/plog/copy-the-current-line-in-vs-code</link><description>In VS Code you can copy the current line without first selecting it. Cmd-C without selection defaults to whole line.</description><pubDate>Thu, 12 Mar 2026 15:07:52 +0000</pubDate><guid>http://www.peterbe.com/plog/copy-the-current-line-in-vs-code</guid></item><item><title>logger.error or logger.exception in Python</title><link>http://www.peterbe.com/plog/logger.error-or-logger.exception</link><description><p>Consider this Python code:</p><pre><code class="hljs"><span class="hljs-keyword">try</span>: <span class="hljs-number">1</span> / <span class="hljs-number">0</span><span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e: logger.error(<span class="hljs-string">&quot;An error occurred while dividing by zero.: %s&quot;</span>, e)</code></pre> <p>The output of this is:</p><pre><code class="hljs">An error occurred while dividing by zero.: division by zero</code></pre> <p>No traceback. Perhaps you don't care because you don't need it.<br />I see code like this quite often and it's curious that you even use <code>logger.error</code> if it's not a problem. And it's curious that you include the stringified exception into the logger message.</p><p>Another common pattern I see is use of <code>exc_info=True</code> like this:</p><pre><code class="hljs"><span class="hljs-keyword">try</span>: <span class="hljs-number">1</span> / <span class="hljs-number">0</span><span class="hljs-keyword">except</span> Exception: logger.error(<span class="hljs-string">&quot;An error occurred while dividing by zero.&quot;</span>, exc_info=<span class="hljs-literal">True</span>)</code></pre> <p>Its output is:</p><pre><code class="hljs">An error occurred while dividing by zero.Traceback (most recent call last): File &quot;/Users/peterbengtsson/dummy.py&quot;, line 23, in &lt;module&gt; 1 / 0 ~~^~~ZeroDivisionError: division by zero</code></pre> <p>Ok, now you get the traceback and the error value (<code>division by zero</code> in this case).</p><p>But a more convenient function is <code>logger.exception</code> which looks like this:</p><pre><code class="hljs"><span class="hljs-keyword">try</span>: <span class="hljs-number">1</span> / <span class="hljs-number">0</span><span class="hljs-keyword">except</span> Exception: logger.exception(<span class="hljs-string">&quot;An error occurred while dividing by zero.&quot;</span>)</code></pre> <p>Its output is:</p><pre><code class="hljs">An error occurred while dividing by zero.Traceback (most recent call last): File &quot;/Users/peterbengtsson/dummy.py&quot;, line 9, in &lt;module&gt; 1 / 0 ~~^~~ZeroDivisionError: division by zero</code></pre> <p>So it's sugar for <code>logger.error</code>.</p><p>Also, a common logging config is something like this:</p><pre>import logging logger = logging.getLogger(__name__)logging.basicConfig( format=&quot;%(asctime)s - %(levelname)s - %(name)s - %(message)s&quot;, level=logging.ERROR)</pre> <p>So if you use <code>logger.exception</code> what will it print? In short, the same as if you used <code>logger.error</code>. For example, with the <code>logger.exception("An error occurred while dividing by zero.")</code> line above:</p><pre><code class="hljs">2026-03-06 10:45:23,570 - ERROR - __main__ - An error occurred while dividing by zero.Traceback (most recent call last): File &quot;/Users/peterbengtsson/dummy.py&quot;, line 12, in &lt;module&gt; 1 / 0 ~~^~~ZeroDivisionError: division by zero</code></pre> <h3 id="bonus-add_note"><a class="toclink" href="#bonus-add_note">Bonus - <code>add_note</code></a></h3><p>You can, if it's applicable, inject some more information about the exception. Consider:</p><pre><code class="hljs"><span class="hljs-keyword">try</span>: n / <span class="hljs-number">0</span><span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> exception: exception.add_note(<span class="hljs-string">f&quot;The numerator was <span class="hljs-subst">{n}</span>.&quot;</span>) logger.exception(<span class="hljs-string">&quot;An error occurred while dividing by zero.&quot;</span>)</code></pre> <p>The net output of this is:</p><pre><code class="hljs">2026-03-06 10:48:34,279 - ERROR - __main__ - An error occurred while dividing by zero.Traceback (most recent call last): File &quot;/Users/peterbengtsson/dummy.py&quot;, line 13, in &lt;module&gt; 1 / 0 ~~^~~ZeroDivisionError: division by zeroThe numerator was 123.</code></pre></description><pubDate>Fri, 06 Mar 2026 15:27:44 +0000</pubDate><guid>http://www.peterbe.com/plog/logger.error-or-logger.exception</guid></item><item><title>How to find which git SHA it was when you merged in the default branch</title><link>http://www.peterbe.com/plog/how-to-find-which-git-sha-it-was-when-you-merged-in-the-default-branch</link><description><p>Please tell me there's a better way.</p><p>I'm in a git feature branch. It was branched off of <code>master</code> some time ago. Then I made some commits. Later, I merged in latest <code>master</code>, and after that some more commits. The question is what was the SHA when I merged in latest <code>master</code>? How to find that out?</p><p>Here's what I did:</p><p>I'm in my feature branch and I run:</p><pre><code class="hljs">git <span class="hljs-built_in">log</span> --merges --first-parent</code></pre> <p>It prints this out:</p><pre><code class="hljs">commit 2c010ef6e94d792f25f7309ed83a2d35e41e5051Merge: 24b2a2513d6f e2817859ed02Author: Peter Bengtsson &lt;email&gt;Date: Thu Feb 26 14:06:24 2026 +0000 Merge remote-tracking branch &#x27;origin&#x27; into name-of-my-feature-branch</code></pre> <p>The SHA that was, on <code>master</code>, at the time of merging, was <code>e2817859ed02</code> which is the second thing the <code>Merge: 24b2a2513d6f e2817859ed02</code> line.</p><p>Now, equipped with this I can go back in time <code>git checkout e2817859ed02</code> and on that detached HEAD, I can do things like run (unit) tests to see if the <code>master</code> branch was busted at the time I merged it into my feature branch.</p></description><pubDate>Thu, 26 Feb 2026 17:23:04 +0000</pubDate><guid>http://www.peterbe.com/plog/how-to-find-which-git-sha-it-was-when-you-merged-in-the-default-branch</guid></item><item><title>gg2 branches got 50% faster by a Promise.all</title><link>http://www.peterbe.com/plog/gg2-branches-got-50percent-faster-by-a-promise.all</link><description><p><a href="https://gg2.peterbe.com/">gg2</a> is a command line tool for doing things with Git branches, faster. One of the convenient commands is <code>gg2 branches</code>. It lists your recent branches, sorted by committer-date and for each branch it displays a human-friendly date distance and whether it's already been merged. Looks like this:</p><p><a href="/cache/d9/44/d94443c94b1f1d57abc02f7d4dcb717c.png"><img src="/cache/d9/44/d94443c94b1f1d57abc02f7d4dcb717c.png" class="fullsize"></a></p><p>Turns out, for large repos figuring out which branches have already been merged is quite slow. For example, in this repo it takes half a second to list all merged branches:</p><pre><code class="hljs">$ git branch --all --merged | wc -l669 $ time git branch --all --merged 1&gt;/dev/null real 0m0.490suser 0m0.460ssys 0m0.030s</code></pre> <p>The problem was that <code>gg2 branches</code> also does multiple other <code>git branch</code> listing commands to get information. In particular: get the current branch, figure out the default branch, a list of branch committer dates, and a list of merged branches.<br />All of these async operations were done in serial. With a bit of rearranging, and refactoring, all of these can be turned into this:</p><pre><code class="hljs"><span class="hljs-keyword">const</span> [defaultBranch, currentBranch, dates, isMerged] = <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([ <span class="hljs-title function_">getDefaultBranch</span>(git), <span class="hljs-title function_">getCurrentBranch</span>(git), <span class="hljs-title function_">getAllBranchDates</span>(git), <span class="hljs-title function_">getAllMergedBranches</span>(git),])</code></pre> <p>The <code>getAllMergedBranches(git)</code> function still takes a very long time and I think it lists all remote branches that have been merged. Anyway, now it's only the slowest part of the <code>Promise.all</code> rather than one of multiple.</p><p><strong>UPDATE</strong></p><p>The act of blogging about something can sometimes bring out how wrong you are.<br />What I didn't realize is that I was using <code>git branch --all --merged</code> to know which local branches have already been merged into the default branch. But you can just use <code>git branch --merged</code> to get the same result but much much faster.</p><p><a href="https://github.com/peterbe/gg2/pull/101">Correcting for this</a>, on a large repo, the average time to execute <code>gg2 branches</code> went from median 541ms to median 188ms. So that's 3x speed improvement.</p></description><pubDate>Mon, 16 Feb 2026 13:17:09 +0000</pubDate><guid>http://www.peterbe.com/plog/gg2-branches-got-50percent-faster-by-a-promise.all</guid></item><item><title>xbar-my-prs Info about your GitHub PRs in the macOS menu bar</title><link>http://www.peterbe.com/plog/xbar-my-prs</link><description><p><a href="https://github.com/peterbe/xbar-my-prs">xbar-my-prs</a> is a plugin I wrote for something called <a href="https://xbarapp.com/">xbar</a> which is a macOS app that let's you write scripts that then show up in the macOS menu bar. Mine is about fetching information about my recent GitHub pull requests.</p><p>A picture of it in action:</p><p><a href="/cache/b8/f6/b8f6c9b52314c6a7674ac86d44fc8a46.png"><img src="/cache/b8/f6/b8f6c9b52314c6a7674ac86d44fc8a46.png" class="fullsize" style="width:70%"></a></p><p>Every minute, it does this GitHub search: <a href="https://github.com/search?q=is%3Apr+is%3Aopen+author%3A%40me+sort%3Aupdated+&amp;type=pullrequests"><code>is:pr author:@me sort:updated</code></a></p><p>Then it parses the results and attempts to display a neat summary.</p><p>And each time it runs, it stores a copy of the result in the <code>/tmp</code> directory so that it can compare what the result became compared to before. So if something has changed in the last minute, it displays a summary of the change(s). Example:</p><p><a href="/cache/d5/d5/d5d5ee05f145495eb912840129a5b20b.png"><img src="/cache/d5/d5/d5d5ee05f145495eb912840129a5b20b.png" class="fullsize" style="width:60%"></a></p><h3 id="how-you-can-get-it-too"><a class="toclink" href="#how-you-can-get-it-too">How you can get it too</a></h3><p>First you have to install <a href="https://xbarapp.com/">xbar</a> onto your macOS.</p><p>At the time of writing you can't install it with Homebrew. Working on that. But all you need is <code>just</code> and Bun and then you can install it like this:</p><pre><code class="hljs">git <span class="hljs-built_in">clone</span> https://github.com/peterbe/xbar-my-prs.git &amp;&amp; <span class="hljs-built_in">cd</span> xbar-my-prsjust installjust ship</code></pre> <p>If all goes well, it should have crated an executable file at:</p><pre><code class="hljs">~/Library/Application\ Support/xbar/plugins/my-prs.1m.bin</code></pre> <p>on your system. And that should be all xbar needs to know there's a new plugin to run.</p></description><pubDate>Thu, 29 Jan 2026 18:37:54 +0000</pubDate><guid>http://www.peterbe.com/plog/xbar-my-prs</guid></item><item><title>Optimizing Bun compiled binary for gg2</title><link>http://www.peterbe.com/plog/optimizing-bun-compiled-binary-for-gg2</link><description><p><strong>tl;dr; adding <code>--bytecode</code> and <code>--production</code> to <code>bun build --compile</code> gives a marginal boost.</strong></p><p>I have a CLI tool called <a href="https://gg2.peterbe.com/">gg2</a> which is written in TypeScript and using Bun <strong>compiled to a single executable binary</strong> that can be <a href="https://www.peterbe.com/plog/gg2-is-now-installable-with-homebrew">installed with Homebrew</a>. It's fast. <a href="https://www.peterbe.com/plog/gg2-is-now-installable-with-homebrew">Only 17% slower than GitHub's Go-compile <code>gh</code> CLI</a>. But can it get even faster?</p><p>I read about <a href="https://bun.com/docs/bundler/bytecode">Bytecode Caching</a> which is supposed to make the executable "dramatically improved". Hmm. Let's see. There's also the <code>--production</code> option, which according to <code>Set NODE_ENV=production and enable minification</code>. Not convinced it's doing much but let's try.</p><p>I compiled the binary 4 different ways:</p><ul><li><code>bun build src/index.ts --target=bun --compile --outfile out/gg</code></li><li><code>bun build src/index.ts --target=bun --compile --outfile out/gg-bytecode --bytecode</code></li><li><code>bun build src/index.ts --target=bun --compile --outfile out/gg-production --production</code></li><li><code>bun build src/index.ts --target=bun --compile --outfile out/gg-bytecode-production --production --bytecode</code></li></ul><p>Then I ran <code>hyperfine</code> like this:</p><pre><code class="hljs">hyperfine &quot;./out/gg --version&quot; &quot;./out/gg-bytecode --version&quot; &quot;./out/gg-production --version&quot; &quot;./out/gg-bytecode-production --version&quot; --warmup 6</code></pre> <p>The results are as follows:</p><table><thead><tr><th>BINARY</th><th>TIME (ms, smaller is better)</th><th>SIZE (MB)</th></tr></thead><tbody><tr><td>gg</td><td>87</td><td>58</td></tr><tr><td>gg-bytecode</td><td>81</td><td>60</td></tr><tr><td>gg-production</td><td>82.4</td><td>55</td></tr><tr><td>gg-bytecode-production</td><td>79.6</td><td>60</td></tr></tbody></table><p><a href="/cache/c7/1f/c71f48b8b4ab4acb5c455a140d0301da.png"><img src="/cache/c7/1f/c71f48b8b4ab4acb5c455a140d0301da.png" class="fullsize"></a></p><p>(<em>sorry for being terrible at using Excel to draw charts</em>)</p><h3 id="conclusion"><a class="toclink" href="#conclusion">Conclusion</a></h3><p>I'm skeptical. The <code>hyperfine</code> often complains about statistical outliers so you have to run it with warmup and re-run it till it doesn't complain.</p><p>One caveat with using <code>--bytecode</code> is that the compiled code is not re-usable across <strong>different versions of bun</strong> but I don't understand how that's applicable if it's a executable binary that is shipped outside of Bun.</p><p>I'm going to add <code>--bytecode</code> and <code>--production</code> to the next release of <a href="https://github.com/peterbe/gg2"><code>gg2</code></a>.</p></description><pubDate>Tue, 13 Jan 2026 16:06:09 +0000</pubDate><guid>http://www.peterbe.com/plog/optimizing-bun-compiled-binary-for-gg2</guid></item></channel></rss>If you would like to create a banner that links to this page (i.e. this validation result), do the following:
Download the "valid RSS" banner.
Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)
Add this HTML to your page (change the image src attribute if necessary):
If you would like to create a text link instead, here is the URL you can use:
http://www.feedvalidator.org/check.cgi?url=http%3A//www.peterbe.com/rss.xml