Nishanth Shanmugham

Read this first

Joyful shell programs

Shell programs are joyful. Today I wrote one that runs git push in all of the directories that I tend to work in—it’s useful for when I’m working offline and later want to push all of my work when I have a network connection.

! /bin/zsh

 usage: pushall [<directories.txt>]

 The pushall command runs git-push in the directories listed—one per
 line—in the supplied file. Variables in the file will be
 expanded using the environment. If no file is supplied, pushall reads
 from the default config file at $HOME/.config/pushall/directories.

set -uo pipefail

push() {
    out="$(2>&1 { cd "$1" && git push; })"
    printf "%s: %s\n" "$(basename "$1")" "$out"
}

main() {
    file="${1:-$HOME/.config/pushall/directories}"
    while read -r d; do
        push "${(e)d}" &
    done < "$file"
    wait
}

main "$@"

It’s especially joyful when you make the program use a config file:

% cat
...

Continue reading →


Start tmux automatically

I wanted to start tmux—the terminal multiplexer—automatically when I opened a new terminal window.

I had some specific requirements however. For instance, I wanted to have a “main” tmux session that new windows should try to attach to. And if I was already attached to the main tmux session, then I wanted any subsequent new terminal windows to create new unnamed sessions (this way each window has its own independent session).

The requirements

  1. If the “main” session doesn’t exist, create a new session named “main”.
  2. If the “main” session exists and there are zero clients attached to it, attach to it ourselves.
  3. If the “main” session exists and there is already a client attached to it, create a new unnamed session.

I wrote a script to do this, since it seems like tmux’s built-in subcommands and flags can’t account for these requirements on their own. The command tmux new -A -s main...

Continue reading →


Hibernate in Fedora

I spent time yesterday making HybridSleep and Hibernate work in Fedora 32 on a ThinkPad X1 Carbon with an encrypted disk.

I don’t ever plan to manually use hybrid-sleep or hibernate; I’d instead just lock my screen or close the laptop lid. So why bother to have these work?

It’s because hybrid sleep and hibernate are useful to have when battery power runs out. You’ll likely find in /etc/UPower/UPower.conf that your machine attempts hybrid-sleep, hibernate, and a full shutdown, in this order, when the battery runs out. If you don’t want to lose working state, you need either a working hibernate or a working hybrid-sleep.

$ cat /etc/UPower/UPower.conf | tail -n 3
 If HybridSleep isn't available, Hibernate will be used
 If Hibernate isn't available, PowerOff will be used
CriticalPowerAction=HybridSleep

How to use this guide

There are typically 5-6 issues. Each of these is described...

Continue reading →


Vim keybindings on a 60% keyboard

Keyboard on a wooden table

I’m using a 60% keyboard—the WASD VP3.

The built-in default mappings use Fn + ijkl as cursor keys.

Fn + i: up
Fn + k: down
Fn + j: left
Fn + l: right

But I prefer vim-like mappings:

Fn + k: up
Fn + j: down
Fn + h: left
Fn + l: right

Thankfully, the VP3 has 3 user-programmable layers—accessed by Fn + {<,>,?}. So I programmed the first of these layers to use the vim movement bindings. (I also then reprogrammed Fn + i to represent Home, which previously represented by Fn + h, would be lost in the new bindings otherwise.)

Steps

Here’s a link to the keyboard manuals and, as an example, the steps I followed to program Fn + j to represent the down cursor key instead of the original left cursor key. You should repeat these steps for each of the keys.

  1. Optionally reset all your previous programming by pressing Right Alt + Left Alt. Wait until the left LED beneath the spacebar stops...

Continue reading →


Kindle font

I’m liking reading on a Kindle.

I really like the feel of electronic paper technology: the screen lighting, the slow turning of pages, and the black & white colors. I’m also thankful for the ability to quickly get samples of books to see if I would be interested in the full books.

I recently learned that you can add custom fonts to Kindle. Palatino is my favorite of the built-in fonts, but custom ones have made reading more enjoyable. I’m really liking Linux Libertine O at:

  • size 6,
  • bold level 2,
  • tightest line spacing, and
  • medium margins.

This is what it looks like:

Kindle held in a human hand

Continue reading →


Properly typed omit function in TypeScript

Writing a properly typed omit function requires a few type casts. I think this is the cleanest possible way (I’d be thrilled to learn of a cleaner way), and it doesn’t include an allocation for an Object.keys(obj) array.

function omit<T, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K> {
    const ret = {} as Omit<T, K>
    const omitKeys = new Set(keys)

    for (const k in obj) {
        const key = k as unknown as K
        if (omitKeys.has(key)) {
            continue
        }
        const presentKey = key as unknown as Exclude<keyof T, K>
        ret[presentKey] = obj[presentKey]
    }

    return ret
}

Let’s look at the necessity* and the safety of each type cast.

const ret = {} as Omit<T, K>

This is necessary at some point before returning. Typing it early (instead of casting later in the return statement) also assists in typing at other portions of the function. This...

Continue reading →


Accidental removal of side-effect imports

Go has the ability to import a package purely for the package’s side-effects (i.e. you don’t actually use a symbol from the package in your code, but just want side-effects introduced by the package).

As an example, look at package expvar in the standard library. Importing the package registers a HTTP handler at “/debug/vars” that prints the state of published vars, as a side-effect of importing the package.

The importing source file may not even necessarily reference any symbols in package expvar. However, this means that it’s easy to lose the import during refactoring or when running goimports(1) on the file.

To avoid losing the import, when you import a package for its side-effects, import it with an underscore, so that it isn’t removed by goimports.

import _ "expvar"

If you’re importing the same package for normal use, import it twice. This way, the _ side-effect import...

Continue reading →


A stricter Omit type

TypeScript provides globally available utility types that are useful when manipulating and transforming types. One of the types, Omit can be used to define a new type with specific properties removed from an existing type. It works like this:

type Song = {
  album: string
  artist: string
  title: string
  loved: boolean
}

type X = Omit<Song, "title" | "loved">
//
// {
//   album: string
//   artist: string
// }

A thing I’ve disliked about the built-in Omit type is that providing a non-existent property to Omit doesn’t result in a compile-time error.

As an example, if we renamed the title property to name in the Song type, the code still compiles successfully.

type Song = {
  album: string
  artist: string
  name: string // renamed
  loved: boolean
}

type X = Omit<Song, "title" | "loved"> 
// compiles successfully though `title` doesn't exist on SongProps
//
// {
//   album:
...

Continue reading →


A guide to using built-in TLS in Redis

Redis version 6 added TLS as a built-in feature. This makes me super happy, because I’m now able to use Redis as a store with my applications running on App Engine considering that the traffic is encrypted, without additional tools or a paid Redis Cloud plan.

Keep in mind that you will have to build Redis with TLS support at compile time. See https://redis.io/topics/encryption.

I couldn’t find an end-to-end example for setting up TLS (not very surprising considering that the feature was released April 30 this year). So here we go.

Generate cert files

To use TLS with Redis, you’ll have to generate:

  1. a certificate-key pair for the server (redis.crt, redis.key), and
  2. a root CA certificate (ca.crt)

The TL;DR is that you run

$ FQDN=<redis-server-ip> make generate

with this Makefile:

FQDN ?= 127.0.0.1
OUTDIR ?= tls
 may be /etc/pki/tls in some machines.
 use `openssl version -a |
...

Continue reading →


Parallel tests in Go

Go surprises me with how simple it makes things. Today, it was parallel testing. In other languages I’ve used, it may require third-party packages, require complicated syntax, or may just not be possible. In Go, it’s as simple as:

import "testing"

func TestMeow(t *testing.T) {
    t.Parallel()
    // test logic here
}

func TestRoar(t *testing.T) {
    t.Parallel()
    // test logic here
}

func TestBark(t *testing.T) {
    // test logic here
}

Tests marked with t.Parallel() will execute in parallel with other tests also marked with t.Parallel(). So the tests TestMeow() and TestRoar() will run in parallel, but TestBark() will not.

In case you want to run tests serially despite marking them parallel, you can set the test.parallel flag instead of commenting out each t.Parallel() call.

$ go test -test.parallel 1

Continue reading →