Building Tools That Build Tools

Apple MacBook with program source code displayed

Photo by Christopher Gower on Unsplash

I have a macOS app that builds, signs, notarises, and releases other macOS apps to GitHub. One of those apps is itself the release manager. It can release itself.

This is either peak efficiency or a warning sign. I’m choosing to see it as the former.

The stack

Here’s what the Jorvik Software toolchain looks like today:

Jorvik Release Manager sits at the top. It’s a SwiftUI desktop app with a 10-stage pipeline: preflight, build, verify, sign, verify signing, notarise, staple, verify notarisation, package, and release. It handles Xcode projects, Swift Package Manager builds, and raw swiftc compilations. It talks to Apple’s notary service, creates GitHub releases, and uploads assets. One click from source code to published release.

JorvikKit is a shared component library — not a framework, just a directory of Swift files that gets copied into each app. It provides menu bar styling, about windows, settings views (with launch-at-login and auto-update), keyboard shortcut recording, and GitHub-based update checking. Every menu bar app I build starts with JorvikKit and adds its own logic on top.

Jorvik Notes Editor manages blog posts. It’s a three-pane markdown editor with live preview, WYSIWYG mode, and deployment to both Docker staging and Cloudflare Pages.

Jorvik Web Editor manages everything else on the website — HTML, CSS, JavaScript, configuration files. Same three-pane layout, but with syntax highlighting, text transforms, HTML validation, and a detachable preview window.

And then there are the apps themselves: HyperCaps, QuitProtect, ScreenLock, WindowPin, MenuTidy, ClipMan, CalendarUpcoming, ActiveSpace, RainbowApple, and ASCII Saver.

The meta layer

What makes this interesting isn’t any single tool. It’s how they combine.

The Release Manager can release JorvikKit apps. JorvikKit apps include an auto-update checker that pulls from GitHub releases — the same releases that the Release Manager creates. The Notes Editor deploys blog posts about the apps. The Web Editor manages the product pages for the apps. The Release Manager releases the editors.

It’s tools all the way down.

The Release Manager’s latest addition: when deploying to a GitHub repository that doesn’t exist yet, it now asks whether to create a public or private repo and does it automatically before proceeding with the release. This means that brand new apps can go from “first line of code” to “published on GitHub with a signed, notarised release” without me ever opening a browser or shell.

Why build instead of buy?

The obvious question. There are existing tools for all of this. Fastlane for releases. VS Code for web editing. Bear or iA Writer for blog posts.

The answer is control and integration. My release pipeline knows about my signing identity, my notary profile, my GitHub organisation, and my Cloudflare account. My web editor knows about my Docker staging setup, my site structure, and my deployment credentials. My notes editor knows about my blog’s frontmatter format and build system.

Each tool does exactly what I need and nothing else. There’s no configuration to maintain, no plugins to update, no subscription to pay, and no telemetry to worry about. When something breaks, I fix it in Swift and rebuild. When I need a new feature, I add it.

The total codebase for all of this tooling is perhaps 8,000 lines of Swift. That’s smaller than most npm dependency trees.

The compound effect

Every tool I build makes the next one cheaper to build. JorvikKit saves me an hour per app. The Release Manager saves me 30 minutes per release. The web editor spares me the context-switch to a terminal for every deployment.

These savings compound. Yesterday I built four apps. Not because I’m fast, but because the infrastructure handles everything that isn’t the core idea. Writing a CGEvent tap to intercept commandQ is interesting work. Setting up code signing and notarisation for the fifteenth time is not.

Build the thing that builds the thing. Then build the thing that releases the thing that builds the thing. At some point the recursion pays for itself, and then some.