RSSAmplifier

Blog

Joel Kitching DOT com on Joel Kitching

Recent content in Joel Kitching DOT com on Joel Kitching

joelkitching.comRSS feed ↗20 posts

Latest posts

Replacing nvm with fnm for faster shell startup

If you’ve used nvm (Node Version Manager) for any length of time, you’ve probably noticed the shell startup delay. On my machine, nvm adds roughly 300-500ms to every new terminal—enough to feel sluggish. The common workaround is lazy loading. oh-my-zsh even has built-in support for it: zstyle ':omz:plugins:nvm' lazy yes NVM_LAZY_LOAD = true This defers nvm initialization until…

Split DNS for Tailscale MagicDNS + NextDNS on Linux

Disclaimer: This post was written by Claude with my direction! We’re all in the midst of recalibrating our ethical compass when it comes to programming and writing with LLMs. Since this post is intended as a reference for future me, it felt okay that I was simply a collaborator and editor. This document describes a robust DNS configuration for Tailscale on Linux that provides: MagicDNS for…

Arch Linux: inhibiting sleep triggered by lid switch

Sometimes I want to run long jobs overnight on my laptop, or even charge my phone while the laptop is closed in my backpack (getting USB to have power when suspended is another longstanding problem). For quite a while, I tried different variations of gnome-session-inhibit and systemd-inhibit . But they never worked properly! Finally, I found this gem on the Arch Linux forum: Systemd-inhibit…

The |& operator: piping stderr since 1978

When running a command, it’s often useful to save output to a file while also displaying it on the terminal. The tee command handles this elegantly: ./my-script.sh | tee output.log But this only captures stdout. To also capture stderr, the traditional approach is: ./my-script.sh 2> & 1 | tee output.log I recently learned about the fancy shorthand operator |& , which does the same:…

Hugo hides posts marked with current date

I had uploaded a post marked with the current date to GitHub, which has an action set up to rebuild Hugo and post to GitHub’s static page hosting. The post didn’t show. I assumed it was because their server was in a different timezone (e.g. PST) and was interpreting the dates as such. But then I loaded up a local server with hugo server and the post was not listed there either! It…

Google Photos availability of ZIP exports

Today, I was in the process of exporting a few batches of files from Google Photos. You select up to 500 photos or videos, hit “Download”, wait a minute or so (depending on the size), and your browser will start downloading the ZIP. These are fairly large files (around 8 GB each in my case), and I was just heading out the door, so I had to cancel a few of them. Later on, when I pulled…

Sharing fonts from host to docker instance

As part of writing html_to_pdf.sh for programatically printing a webpage to PDF format, I encountered the problem of fonts appearing differently after having been output as PDF. Further investigation revealed the alpine-chrome Docker image did not have the fonts being used in the HTML document. A simple solution—replace /usr/share font directories in the Docker image with those of the host:…

Printable HTML/CSS template for resume

The resume I used for the majority of my life is a relic from university times, when I joined the Computer Science co-op program and applied for oodles of internship placements. Back in the day, I designed the layout in Microsoft Word, with careful formatting constructed of tables, blank lines with font size 1, and painstakingly adjusted margins. Word ran great with wine, and I even had a shell…

Installing Magisk and LSPosed on emulated Android

After attempting many different ways of getting Magisk to run on Android emulators (including Waydroid and the like), the only way that worked is using the official emulator from Google, along with the instructions from the following Gist: tothi/magisk_ramdisk_patch_avd.sh Notes userdata.img needs to be recreated when changing size via the emulator -partition-size flag, so pick its size carefully!…

Convert iPhone Live Photo to Google Photos motion photo

Google Photos has two methods of handling motion photos: If the photo was uploaded by an Android device, it will already be in Google’s custom format, which is essentially a JPEG file with a video file concatenated to the end. If the photo was uploaded by an iPhone, it will be stored as two separate files on Google servers: HEIC and MOV. When downloading motion photos and re-uploading them…

Restarting a process when a signal is received

In larger applications, there is usually a CI/CD process, which tests, builds, and deploys new versions of the software. Smaller applications typically start out with a simpler solution. In this case, I have an application running in a screen session, and I want a git post-receive hook to automatically update its code and restart the process. Is it possible to write a one-line Bash wrapper which…

Using git clean in a deployment hook

A typical post-receive hook for updating a target directory based on the most recent commit might look like this: #!/bin/bash export GIT_WORK_TREE = '../myproject' git checkout -f master However, this does not remove any untracked files or directories in ../myproject . When files are removed or renamed in later commits, their remnants will remain untouched in the deployment directory. How can we…

Terminating a command early on matching stdout conditions

Match and exit This StackExchange question provides a snippet which runs a subprocess, saves its PID, and then ends the process when a certain output string is encountered in its output. sh -c 'echo '$$'; exec stdbuf -oL '$0' '$@'' my-program with its args | ( IFS = read -r pid && sed '/Was exported to:/q' && kill -s PIPE ' $pid ' ) Match and exit, with short-circuit I encountered a situation in…

Running SQL queries on CSV data

I was recently working with some CSV files pulled from a Samsung Health dump. I wanted to run a few queries on the data in order to familiarize myself with their format and contents. SQL seemed like a natural choice, and it turns out there is a myriad of different ways to directly query CSV data with SQL. A few notes: The first line of the file is extraneous information that Samsung Health…

Creating a JSON database of photos and albums using Google Photos API

It turns out that using the Google Photos API is not particularly suited to organizing your photos collection. On the other hand, the API makes it easy to create a listing of all photos and albums in your collection. For the case of creating a simple file-based JSON database, I wanted a command-line driven solution, so I used photoslibrary1-cli . Authentication The photoslibrary1-cli documentation…

Unreasonable limitations of Google Photos API

Beware: Google Photos API is not suitable for reorganizing your photo collection. From Google for Developers documentation : batchAddMediaItems : Note that you can only add media items that have been uploaded by your application to albums that your application has created. batchRemoveMediaItems : Note that you can only remove media items that your application has added to an album or that have…

Motion photos not recognized by Google Photos in shared albums

After adding a motion photo into a shared album, Google Photos sometimes fails to show the triangular ▶ play button. Right from the horse’s mouth: Google Photos can be a bit buggy with Live Photos in shared albums - the best way I’ve found to combat this is to just upload to Google Photos first (don’t add them to a shared album just yet), wait a bit (can be anywhere between a few minutes to…

Coding a simple algorithm with ChatGPT

Lately, it’s almost impossible to open up Hacker News without encountering an article extolling the virtues of LLM-assisted programming. I wanted to give it a try—but I decided to start with something small. Problem definition Given an input array of 0’s and 1’s, produce an array where output[i] gives the consecutive count of input[i] up until position i . For example:…

GNOME development with x11docker containers

In A brief foray into GNOME extension development , we looked at the general process of updating a GNOME extension to work on a newer version of GNOME. The most obvious development environment for working on GNOME extensions is an active GNOME session. But I encountered numerous pain points with this setup: Reloading extensions: This is possible in X by reloading gnome-shell with Alt+F2 and r ,…

A brief foray into GNOME extension development

I used to be an exclusive user of tiling window managers, namely: xmonad . When I converted over to the GNOME world, I tried all of the window tiling extensions and settled on the excellent PaperWM . But PaperWM, as with most GNOME extensions, struggled to keep up with the rapid pace of GNOME releases, and I was stuck on vanilla GNOME for some time. Recently, Arch pushed the GNOME 43 release, and,…