mirror of
https://github.com/skoelle/kctl-tui.git
synced 2026-09-17 20:10:24 +00:00
feat: add interactive update check on startup
When starting kctl-tui without a subcommand, check GitHub Releases for a newer version and prompt the user to update. - Silent skip for dev builds and network errors - Prompt: 'Update now? [y/N]' - On 'y': apply update, then start TUI - On 'N'/Enter: start TUI immediately
This commit is contained in:
@@ -83,6 +83,8 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
checkForUpdateInteractive(verbose)
|
||||
|
||||
m := newFullModel()
|
||||
p := tea.NewProgram(m, tea.WithAltScreen())
|
||||
if _, err := p.Run(); err != nil {
|
||||
|
||||
+101
-14
@@ -4,11 +4,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/creativeprojects/go-selfupdate"
|
||||
)
|
||||
|
||||
@@ -29,6 +32,103 @@ func runUpdate(verbose bool) error {
|
||||
fmt.Printf("Current version: %s\n", current)
|
||||
fmt.Println("Checking for updates...")
|
||||
|
||||
rel, err := detectLatest()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check for updates: %w", err)
|
||||
}
|
||||
|
||||
if rel == nil {
|
||||
fmt.Println("Already up-to-date.")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Found version %s. Updating...\n", rel.Version())
|
||||
|
||||
if err := applyUpdate(rel); err != nil {
|
||||
return fmt.Errorf("update failed: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Updated from %s to %s\n", current, rel.Version())
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkForUpdateInteractive checks for a new version and prompts the user to update.
|
||||
// Returns true if an update was applied.
|
||||
func checkForUpdateInteractive(verbose bool) bool {
|
||||
if version == "dev" {
|
||||
return false
|
||||
}
|
||||
|
||||
if verbose {
|
||||
selfupdate.SetLogger(&verboseLogger{})
|
||||
}
|
||||
|
||||
rel, err := detectLatest()
|
||||
if err != nil {
|
||||
// Silently ignore network errors — don't block startup
|
||||
if verbose {
|
||||
fmt.Fprintf(os.Stderr, "Update check failed: %v\n", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if rel == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
current, _ := semver.NewVersion(version)
|
||||
newVersion := rel.Version()
|
||||
|
||||
if current != nil && !rel.GreaterThan(current.String()) {
|
||||
return false
|
||||
}
|
||||
|
||||
fmt.Printf("New version %s available (current: %s). Update now? [y/N] ", newVersion, version)
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
answer, _ := reader.ReadString('\n')
|
||||
answer = strings.TrimSpace(strings.ToLower(answer))
|
||||
|
||||
if answer != "y" && answer != "yes" {
|
||||
return false
|
||||
}
|
||||
|
||||
fmt.Println("Updating...")
|
||||
if err := applyUpdate(rel); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Update failed: %v\n", err)
|
||||
return false
|
||||
}
|
||||
|
||||
fmt.Printf("Updated to %s. Starting kctl-tui...\n", newVersion)
|
||||
return true
|
||||
}
|
||||
|
||||
func detectLatest() (*selfupdate.Release, error) {
|
||||
source, err := selfupdate.NewGitHubSource(selfupdate.GitHubConfig{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to init GitHub source: %w", err)
|
||||
}
|
||||
|
||||
updater, err := selfupdate.NewUpdater(selfupdate.Config{
|
||||
Source: source,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create updater: %w", err)
|
||||
}
|
||||
|
||||
repo := selfupdate.ParseSlug(githubSlug)
|
||||
rel, found, err := updater.DetectLatest(context.Background(), repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
func applyUpdate(rel *selfupdate.Release) error {
|
||||
source, err := selfupdate.NewGitHubSource(selfupdate.GitHubConfig{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to init GitHub source: %w", err)
|
||||
@@ -41,20 +141,7 @@ func runUpdate(verbose bool) error {
|
||||
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
|
||||
return updater.UpdateTo(context.Background(), rel, "")
|
||||
}
|
||||
|
||||
type verboseLogger struct{}
|
||||
|
||||
@@ -3,6 +3,7 @@ module github.com/skoelle/kctl-tui
|
||||
go 1.25.12
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.5.0
|
||||
github.com/charmbracelet/bubbles v0.21.1
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/creativeprojects/go-selfupdate v1.6.0
|
||||
@@ -12,7 +13,6 @@ require (
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.23.2 // indirect
|
||||
github.com/42wim/httpsig v1.2.4 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.5.0 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||
|
||||
Reference in New Issue
Block a user