Back to Examples

Self-Updating CLI Tool

Build a command-line tool that can update itself automatically with version checking.

85 min
Advanced
GoRustPython

Overview

In this example, we'll create a command-line tool that can check for updates and replace itself with a new version when available. This is particularly useful for distributing tools that need to stay up-to-date.

Key Features

  • Version checking against a remote repository
  • Secure binary verification
  • Atomic updates to prevent corruption
  • Cross-platform compatibility
  • Clean rollback on failed updates

Implementation Steps

  1. Version Checking

    Implement a function to check the current version against a remote version endpoint.

  2. Download New Version

    Securely download the new binary to a temporary location.

  3. Verification

    Verify the downloaded binary's checksum and signature.

  4. Atomic Replacement

    Replace the current binary with the new version in an atomic operation.

  5. Restart

    Restart the application with the new version if needed.

Example Code (Go)

package main

import (
  "crypto/sha256"
  "fmt"
  "io"
  "net/http"
  "os"
  "os/exec"
  "path/filepath"
  "runtime"
)

func checkForUpdate(currentVersion string) (bool, string, error) {
  // Implementation would check a remote endpoint
  // and return if an update is available with the new version
  return false, "", nil
}

func downloadUpdate(version string) (string, error) {
  // Implementation would download the new binary
  return "", nil
}

func verifyChecksum(filePath, expectedChecksum string) bool {
  // Implementation would verify the file's checksum
  return true
}

func replaceBinary(newBinaryPath string) error {
  // Implementation would replace the current binary
  return nil
}

func main() {
  currentVersion := "1.0.0"
  
  updateAvailable, newVersion, err := checkForUpdate(currentVersion)
  if err != nil {
    fmt.Printf("Error checking for updates: %v
", err)
    return
  }
  
  if updateAvailable {
    fmt.Printf("Updating to version %s...
", newVersion)
    
    // Download and verify new version
    tempPath, err := downloadUpdate(newVersion)
    if err != nil {
      fmt.Printf("Error downloading update: %v
", err)
      return
    }
    
    // Replace current binary
    if err := replaceBinary(tempPath); err != nil {
      fmt.Printf("Error applying update: %v
", err)
      return
    }
    
    fmt.Println("Update completed successfully!")
  } else {
    fmt.Println("Already up to date!")
  }
}

Concepts Covered

Auto-UpdatesVersion ManagementBinary Distribution