Skip to content

DispatchesΒΆ

govm: Switch Between Go Versions Without the Headache

If you write Go code regularly across multiple projects, you have almost certainly run into this situation: one project pins to Go 1.22, another requires Go 1.23, and a third is cutting edge on whatever just shipped. Installing Go manually, updating your PATH by hand, and keeping track of which binary lives where is tedious and error-prone. A version manager removes all of that friction.

govm is a lightweight, open-source Go version manager built by Melkey. It ships both a polished terminal UI (TUI) and a clean command-line interface, installs Go releases directly from go.dev, and uses a shim-based approach to switch versions without touching your shell configuration after the initial setup. It has become a regular part of my toolbox as an open source developer who routinely jumps between Go releases.

Why You Should Pin GitHub Actions to Commit Hashes

If you have used GitHub Actions, you have almost certainly written something like this:

steps:
  - name: Checkout
    uses: actions/checkout@v6
  - name: Setup Go
    uses: actions/setup-go@v6

It works. It is clean. It is also handing the keys to your CI pipeline to a tag pointer that anyone with push access to those repositories can move at any time, for any reason, without your knowledge.

This post covers what is actually happening when you write @v..., why it matters, and how to fix it in a way that is sustainable long-term. No conspiracy theories required: just a clear-eyed look at how Git references work and what a supply chain attack actually looks like in practice.

Automate a Ubuntu Server Daily Build on a VMware Desktop Hypervisor

tenthirtyam/packer-vmware-desktop-ubuntu-daily automates the build of a baseline Ubuntu Server virtual machine on VMware Fusion 13 or later, or VMware Workstation 17 or later, from the latest Ubuntu daily release using Packer and cloud-init.

Run ./ubuntu-daily.sh and the pipeline discovers the current daily ISO, validates the SHA256 checksum, performs a fully unattended installation via cloud-init autoinstall, takes a snapshot, and cleans up. The output is a ready-to-use Ubuntu Server virtual machine, named by build date and architecture, accessible over SSH.

Under That Kind of Sky

The area of South Georgia I came from did not announce itself as haunted. It called itself ordinary. It was made of fence lines, gravel, ditches, heat, livestock, debt, prayer, rust, and long stretches of road that seemed to vanish into weather. Nothing in it asked to be mythologized. Still, looking back, it feels touched by something darker than hardship alone. Not a ghost story exactly, but a place where ruin had a pulse and memory seemed to live in the ground.

Bulk Delete GitHub Actions Workflow Runs

GitHub Actions accumulates workflow run history quickly. After a few months of active development, a busy repository can have thousands of runs: every push, every pull request, every scheduled job. Most of those runs are no longer useful, but the GitHub web interface only lets you delete one run at a time. If you want to clean up a large backlog, you need a different approach.

This post covers a one-liner that bulk-deletes all workflow runs for a repository using the GitHub CLI (gh) and jq.

CODEOWNERS: Automating Code Review Ownership

Most projects have a moment when someone merges a change to a critical file without the right people ever seeing it. Not because anyone meant to skip the review, but because nothing in the workflow made it obvious who should have been asked. A security-sensitive configuration file, a shared library that dozens of services depend on, a public API contract: all of them can drift in the wrong direction when ownership is implied rather than explicit.

A CODEOWNERS file solves that problem. It maps paths and patterns in your repository to the people and teams responsible for reviewing them. When someone opens a pull request or merge request that touches those paths, the platform automatically requests a review from the designated owners. No manual assignment required, no institutional knowledge needed, and no way to merge without the right sign-off if you enforce it with branch protection.

This post covers what a CODEOWNERS file is, how to construct one, and how to use it effectively in your repositories.

Renaming a Git Tag

Git does not have a rename command for tags. There is no git tag --move or git tag --rename. Tags are immutable references; once created, you cannot change the name of one without creating a new one and removing the old one.

That leaves developers in an awkward position when a tag carries the wrong name. You tagged v2.1.7 as release-2.1.7. The project switched from a release-X.Y.Z convention to vX.Y.Z. A typo slipped through before the push. Whatever the reason, the tag exists in both local and remote repositories, and it needs to be renamed.

The process takes four commands, and each one does a specific job.

Using dev_overrides for Local Terraform Provider Development

When you are building a Terraform provider, the default installation mechanism works against you. Every time you want to test a change, Terraform looks up the provider in a registry. That means you either publish a pre-release to the Terraform Registry on every iteration, configure a private local mirror, or wire up a complex network mirror configuration just to try out a two-line fix. None of those options belong in a tight edit-compile-test loop.

The dev_overrides block in the Terraform CLI configuration file solves this. It tells Terraform to skip the registry entirely for a named provider and load the binary from a local path instead. On macOS and Linux, this file is ~/.terraformrc. On Windows, it is %APPDATA%\terraform.rc.

Rebase. Squash. Merge. Repeat.

You open a pull request. The CI checks pass. A reviewer leaves a comment:

"Please squash your commits and rebase onto the latest main."

If you are new to contributing to open source or working on a team with a structured workflow, that request can feel like an obstacle between you and getting your work merged. It is not. It is a signal that the project cares about its history, and that caring about history is worth your time too.

This post covers the contributor side: what to do to a branch before opening a pull request (or merge request, as GitLab calls it), covering rebase, squash, and sign.

The maintainer side, covering the three GitHub merge strategies and why squash and merge is the right default, is covered in Squash and Merge: A Better Default.

A lot of what follows is inspired by Marc Gasch's Git rebase, squash...oh my!. This post builds on that foundation and connects these practices to the structured commit workflow covered in Conventional Commits: How to Write a Better Git Commit Message and the contributor expectations described in CONTRIBUTING.md: Writing Practical Contribution Guidelines for GitHub Repositories.