mirror of
https://github.com/skoelle/kctl-tui.git
synced 2026-09-17 20:10:24 +00:00
feat: add self-update command
Add 'kctl-tui update' subcommand that checks GitHub Releases for new versions and replaces the running binary. Uses github.com/creativeprojects/go-selfupdate for safe binary replacement with automatic OS/arch detection. Supports --verbose flag for detailed update progress logging.
This commit is contained in:
@@ -70,6 +70,12 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
case "update":
|
||||
if err := runUpdate(verbose); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "kctl-tui update error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown command: %s\n\n", filtered[0])
|
||||
printUsage()
|
||||
@@ -92,6 +98,7 @@ https://github.com/skoelle/kctl-tui
|
||||
Usage:
|
||||
kctl-tui [flags] start the TUI (full navigation mode)
|
||||
kctl-tui doctor check tools, config and connections
|
||||
kctl-tui update update to the latest release
|
||||
kctl-tui config check validate ~/.kctl-tui/config.yaml
|
||||
kctl-tui panel [options] control pane (called internally by tmux)
|
||||
|
||||
@@ -103,6 +110,7 @@ Flags:
|
||||
Examples:
|
||||
kctl-tui # start the TUI
|
||||
kctl-tui doctor # verify everything is installed
|
||||
kctl-tui update # update to the latest version
|
||||
kctl-tui --verbose 2>debug.log # log commands to a file
|
||||
kctl-tui config check # validate config
|
||||
`)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2026 Stefan Koelle (https://stefankoelle.de)
|
||||
// Licensed under the MIT License. See LICENSE file in project root for details.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/creativeprojects/go-selfupdate"
|
||||
)
|
||||
|
||||
const githubSlug = "skoelle/kctl-tui"
|
||||
|
||||
func runUpdate(verbose bool) error {
|
||||
if verbose {
|
||||
selfupdate.SetLogger(&verboseLogger{})
|
||||
}
|
||||
|
||||
current := version
|
||||
if current == "dev" {
|
||||
fmt.Fprintln(os.Stderr, "WARNING: running dev build — cannot compare versions")
|
||||
fmt.Println("Skipping version check. Build from a tagged release to enable self-update.")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Current version: %s\n", current)
|
||||
fmt.Println("Checking for updates...")
|
||||
|
||||
source, err := selfupdate.NewGitHubSource(selfupdate.GitHubConfig{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to init GitHub source: %w", err)
|
||||
}
|
||||
|
||||
updater, err := selfupdate.NewUpdater(selfupdate.Config{
|
||||
Source: source,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create updater: %w", err)
|
||||
}
|
||||
|
||||
repo := selfupdate.ParseSlug(githubSlug)
|
||||
|
||||
rel, err := updater.UpdateSelf(context.Background(), current, repo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update failed: %w", err)
|
||||
}
|
||||
|
||||
if rel != nil && rel.Version() != current {
|
||||
fmt.Printf("Updated from %s to %s\n", current, rel.Version())
|
||||
} else {
|
||||
fmt.Println("Already up-to-date.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type verboseLogger struct{}
|
||||
|
||||
func (l *verboseLogger) Print(v ...any) {
|
||||
fmt.Fprint(os.Stderr, v...)
|
||||
}
|
||||
|
||||
func (l *verboseLogger) Printf(format string, v ...any) {
|
||||
fmt.Fprintf(os.Stderr, format, v...)
|
||||
}
|
||||
|
||||
func init() {
|
||||
log.SetOutput(os.Stderr)
|
||||
}
|
||||
Reference in New Issue
Block a user