Archived
Add install script, release Makefile and self-update
Releases are built locally (no runner) and uploaded to the Gitea generic package registry. install.sh installs submarine plus a sub symlink; `submarine update` replaces the binary after verifying its checksum. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+83
-1
@@ -6,6 +6,8 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
@@ -15,13 +17,27 @@ import (
|
||||
"submarine/internal/submodule/git"
|
||||
"submarine/internal/submodule/mock"
|
||||
"submarine/internal/tui"
|
||||
"submarine/internal/update"
|
||||
)
|
||||
|
||||
// version is set at build time (-ldflags "-X main.version=v1.2.3").
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
if len(os.Args) > 1 {
|
||||
switch os.Args[1] {
|
||||
case "update":
|
||||
os.Exit(runUpdate(os.Args[2:]))
|
||||
case "version", "--version", "-version":
|
||||
fmt.Println("submarine", version)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
useMock := flag.Bool("mock", false, "use built-in mock data instead of a real repository")
|
||||
noFetch := flag.Bool("no-fetch", false, "don't fetch remotes in the background on startup")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintln(flag.CommandLine.Output(), "usage: submarine [--mock] [--no-fetch] [dir]")
|
||||
fmt.Fprintln(flag.CommandLine.Output(), "usage: submarine [--mock] [--no-fetch] [dir]\n submarine update [--check] [--force]\n submarine version")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
@@ -56,9 +72,75 @@ func main() {
|
||||
zone.NewGlobal()
|
||||
defer zone.Close()
|
||||
|
||||
notice := checkForUpdate(ctx, *noFetch || *useMock)
|
||||
|
||||
p := tea.NewProgram(tui.New(ctx, provider, tui.Options{Fetch: !*noFetch}), tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||
if _, err := p.Run(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
select {
|
||||
case v := <-notice:
|
||||
if v != "" {
|
||||
fmt.Fprintf(os.Stderr, "submarine %s is available (you have %s), run: submarine update\n", v, version)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// checkForUpdate looks for a newer release in the background (at most once a
|
||||
// day, cached) and delivers its version, or "" when there is none. Disabled
|
||||
// for dev builds, --no-fetch, --mock and SUBMARINE_NO_UPDATE_CHECK.
|
||||
func checkForUpdate(ctx context.Context, off bool) <-chan string {
|
||||
out := make(chan string, 1)
|
||||
cache, err := os.UserCacheDir()
|
||||
if off || err != nil || !update.Release(version) || os.Getenv("SUBMARINE_NO_UPDATE_CHECK") != "" {
|
||||
out <- ""
|
||||
return out
|
||||
}
|
||||
go func() {
|
||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
v, err := update.New().LatestCached(ctx, filepath.Join(cache, "submarine", "latest-version"))
|
||||
if err != nil || !update.Newer(version, v) {
|
||||
v = ""
|
||||
}
|
||||
out <- v
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
func runUpdate(args []string) int {
|
||||
fs := flag.NewFlagSet("update", flag.ExitOnError)
|
||||
check := fs.Bool("check", false, "only report whether a newer version exists")
|
||||
force := fs.Bool("force", false, "install the latest version even if not newer (also for dev builds)")
|
||||
fs.Parse(args)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||
defer cancel()
|
||||
c := update.New()
|
||||
latest, err := c.Latest(ctx)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "submarine: update check failed:", err)
|
||||
return 1
|
||||
}
|
||||
if !*force && !update.Newer(version, latest) {
|
||||
fmt.Printf("submarine %s is up to date\n", version)
|
||||
return 0
|
||||
}
|
||||
if *check {
|
||||
fmt.Printf("update available: %s (you have %s)\n", latest, version)
|
||||
return 0
|
||||
}
|
||||
if !update.Release(version) && !*force {
|
||||
fmt.Fprintf(os.Stderr, "submarine: this is a %q build; use --force to replace it with %s\n", version, latest)
|
||||
return 1
|
||||
}
|
||||
exe, err := c.Install(ctx, latest)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "submarine: update failed:", err)
|
||||
return 1
|
||||
}
|
||||
fmt.Printf("updated %s: %s -> %s\n", exe, version, latest)
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user