How the no-mistakes Update Checker Notifies Users of New Versions
The no-mistakes CLI spawns a background process on every startup that queries the GitHub Releases API, caches the latest version in a JSON file, and prints a colored notification to stderr if a newer release exists.
The no-mistakes repository implements a self-contained update notification system that runs transparently without blocking command execution. This mechanism checks for new versions by comparing the running binary against the latest GitHub release, ensuring users stay current without manual polling. The entire flow is orchestrated through five distinct steps that balance immediacy with API rate limit protection.
The Notification Flow
The update checker operates through a coordinated foreground-background process that minimizes startup latency while ensuring fresh version data.
Initialize the Updater
When the CLI starts, it immediately calls defaultUpdater in internal/update/update.go. This function creates an updater struct containing the application name, the current binary version (pulled from internal/buildinfo/version.go), a path to a JSON cache file, and writers for stdout and stderr.
Spawn a Background Check
If the binary is not a development build, the updater launches a detached subprocess via internal/update/background.go. This subprocess executes the same binary with the special --update-check flag, allowing the version check to run asynchronously without blocking the main CLI operation.
Query GitHub for Releases
The background process executes u.checkLatest, which utilizes the GitHub Releases API through fetchLatestRelease in internal/update/release.go. The system specifically filters for the newest non-prerelease tag to ensure stability. The result is then written to <cachePath>/update-check.json by the cache layer in internal/update/cache.go.
Cache Reading and Comparison
Back in the foreground process, maybeNotifyAndCheck loads the cached version data early in the startup sequence. It compares the LatestVersion field from the cache against the current binary version to determine if an update is available.
Display the Notification
If a newer version exists and the user is not already running the update command or the --update-check flag, the updater prints a colored message to stderr:
fmt.Fprintf(u.stderrWriter(),
"%sA new version of %s is available: %s -> %s\nRun \"%s update\" to update%s\n",
u.yellow(),
u.appName,
u.currentVersion,
cache.LatestVersion,
u.appName,
u.reset())
The output appears as:
⚠️ A new version of no-mistakes is available: v1.2.2 -> v1.2.3
Run "no-mistakes update" to update
Key Implementation Details
Several implementation choices ensure the update checker remains lightweight and non-intrusive.
Cache File Management – The internal/update/cache.go file stores the JSON payload in update-check.json with fields including LatestVersion and CheckedAt. This prevents hammering the GitHub API on every CLI invocation by respecting a reasonable time window between checks.
Background Flag – The literal --update-check string (defined as backgroundFlag) distinguishes the background runner from normal CLI invocations, ensuring the subprocess terminates after updating the cache rather than executing user commands.
Terminal Colors – The yellow() and reset() helper methods emit standard ANSI color escape sequences, making the notification visually prominent without breaking terminal compatibility.
Daemon Coordination – Before replacing the binary during an actual update, the system calls ensureDaemonUsesCurrentExecutable from internal/update/daemon.go. This verification prevents version mismatches between the CLI binary and any running daemon processes.
Code Examples
Automatic Notification on Startup
When you run any standard command, the notification appears automatically on stderr:
$ no-mistakes status
⚠️ A new version of no-mistakes is available: v1.2.2 -> v1.2.3
Run "no-mistakes update" to update
This notice prints to stderr specifically so it remains visible even when stdout is piped to other programs.
Performing the Update
Users can trigger the actual update process with:
$ no-mistakes update
Downloading v1.2.3…
Extracting…
updated no-mistakes from v1.2.2 to v1.2.3
If active pipeline runs exist, the active_runs.go logic will prompt for confirmation before proceeding with the binary replacement.
Internal Implementation
The following simplified Go code demonstrates how the check is triggered during CLI initialization:
func main() {
// Set up stdout/stderr writers
upd, _ := defaultUpdater(os.Stdout, os.Stderr)
// Run the background check without blocking
go upd.startBackgroundCheck(context.Background())
// Later, after parsing CLI args:
upd.maybeNotifyAndCheck(os.Args[1:])
// Continue with normal command handling…
}
Summary
- The
no-mistakesupdate checker runs a background process on every CLI startup viainternal/update/background.go. - It queries the GitHub Releases API for the latest non-prerelease tag and stores the result in a local JSON cache managed by
internal/update/cache.go. - The foreground process compares cached versions using
maybeNotifyAndCheckininternal/update/update.go. - Notifications print to stderr using yellow ANSI color codes to ensure visibility without interfering with piped output.
- The system includes safeguards in
internal/update/daemon.goto ensure daemon-binary version consistency before performing updates.
Frequently Asked Questions
How often does no-mistakes check for updates?
The background check spawns on every CLI invocation, but the actual GitHub API query respects the cache timestamp stored in update-check.json. If the cache is fresh (typically within a few hours), the background process reads the existing cache rather than hitting the API, preventing rate limit issues while keeping version information relatively current.
Where does no-mistakes store the update cache?
The cache file location is determined during updater initialization in internal/update/update.go. The system writes to a JSON file named update-check.json in a platform-specific cache directory, storing fields like LatestVersion and CheckedAt to track when the last successful API query occurred.
Why does the notification print to stderr instead of stdout?
According to the implementation in internal/update/update.go, the notification specifically uses u.stderrWriter() rather than standard output. This design choice ensures that users see update warnings even when piping command output to files or other processes, and it prevents the notification text from contaminating structured data streams.
What happens if I run no-mistakes update while the daemon is active?
The updater calls ensureDaemonUsesCurrentExecutable from internal/update/daemon.go before replacing the binary. This function verifies that any running daemon instance is using the same executable file that is about to be updated. If version mismatches are detected, the system handles coordination to prevent inconsistent states between the CLI and background daemon processes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →