This commit is contained in:
partisan 2025-02-03 15:52:19 +01:00
commit d9dae02ffc
17 changed files with 1742 additions and 0 deletions

263
main.go Normal file
View file

@ -0,0 +1,263 @@
package main
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
// Import your SPM-based installer code
"spitfire-luncher/spm"
)
// -------------------------------------------------------------------
// GLOBALS
// -------------------------------------------------------------------
var (
copyingInProgress int32
updateStatusMsg string
browserCmd *exec.Cmd
browserMutex sync.Mutex // Protects the lock file map
isBackgroundMode bool
)
// StateFile holds the update state from INI
type StateFile struct {
IsUpdating bool
Progress int32
Stage string // "Idle","Downloading","Decompressing","ReadyToInstall","Installing","Complete","Failed"
}
// -------------------------------------------------------------------
// LOG INIT
// -------------------------------------------------------------------
func init() {
log.SetFlags(0)
log.SetPrefix("[UPDATER] ")
}
// -------------------------------------------------------------------
// INI Helpers
// -------------------------------------------------------------------
func getSpmDir() string {
installDir, err := spm.GetDefaultInstallDir()
if err != nil {
log.Println("Warning: Using '.' for spmDir =>", err)
return "."
}
spmDir := filepath.Join(installDir, "spm")
_ = os.MkdirAll(spmDir, 0755)
return spmDir
}
func getStateFilePath() string {
return filepath.Join(getSpmDir(), "update_state.ini")
}
func WriteState(isUpdating bool, progress int32, stage string) {
content := "[UpdateState]\n" +
fmt.Sprintf("IsUpdating=%v\n", isUpdating) +
fmt.Sprintf("Progress=%d\n", progress) +
fmt.Sprintf("Stage=%s\n", stage)
_ = os.WriteFile(getStateFilePath(), []byte(content), 0644)
}
func ReadState() StateFile {
sf := StateFile{}
data, err := os.ReadFile(getStateFilePath())
if err != nil {
return sf
}
lines := strings.Split(string(data), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "[") {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}
key := strings.TrimSpace(parts[0])
val := strings.TrimSpace(parts[1])
switch key {
case "IsUpdating":
sf.IsUpdating = (val == "true")
case "Progress":
if i, errAtoi := strconv.Atoi(val); errAtoi == nil {
sf.Progress = int32(i)
}
case "Stage":
sf.Stage = val
}
}
return sf
}
// -------------------------------------------------------------------
// BROWSER
// -------------------------------------------------------------------
// LaunchBrowser starts the browser and creates a unique lock file in the spmdir.
func LaunchBrowser() {
spmDir := getSpmDir()
// Generate a unique lock file name (based on timestamp)
lockFileName := fmt.Sprintf("browser_lock_%d.lock", time.Now().UnixNano())
lockFilePath := filepath.Join(spmDir, lockFileName)
// Create the lock file
file, err := os.Create(lockFilePath)
if err != nil {
fmt.Println("Failed to create lock file:", err)
return
}
file.Close()
// Run the browser process and wait for it to finish
fmt.Println("Starting browser...")
err = spm.RunAndWait()
if err != nil {
fmt.Println("Browser process encountered an error:", err)
}
// Remove the lock file after the browser finishes
fmt.Printf("Removing lock file: %s\n", lockFilePath)
err = os.Remove(lockFilePath)
if err != nil {
fmt.Println("Failed to remove lock file:", err)
} else {
fmt.Println("Lock file removed successfully.")
}
}
// IsBrowserRunning checks if there are any browser lock files in the spmdir.
func IsBrowserRunning() bool {
spmDir := getSpmDir()
// Look for lock files in the spmdir
lockFiles, err := filepath.Glob(filepath.Join(spmDir, "browser_lock_*.lock"))
if err != nil {
fmt.Println("Error checking for lock files:", err)
return false
}
// Return true if at least one lock file is found
return len(lockFiles) > 0
}
// -------------------------------------------------------------------
// BACKGROUND SERVICE
// -------------------------------------------------------------------
func runBackgroundUpdater() {
// Delete the update_state.ini file on startup
stateFilePath := getStateFilePath()
if _, err := os.Stat(stateFilePath); err == nil {
err = os.Remove(stateFilePath)
if err != nil {
log.Printf("Failed to delete state file %s: %v\n", stateFilePath, err)
} else {
log.Printf("Deleted state file: %s\n", stateFilePath)
}
} else {
log.Printf("State file does not exist, no need to delete: %s\n", stateFilePath)
}
// Remove all lock files in the spmdir
spmDir := getSpmDir()
lockFilePattern := filepath.Join(spmDir, "browser_lock_*.lock")
lockFiles, err := filepath.Glob(lockFilePattern)
if err != nil {
log.Printf("Failed to scan for lock files in %s: %v\n", spmDir, err)
} else {
for _, lockFile := range lockFiles {
err := os.Remove(lockFile)
if err != nil {
log.Printf("Failed to delete lock file %s: %v\n", lockFile, err)
} else {
log.Printf("Deleted lock file: %s\n", lockFile)
}
}
}
log.Println("Background updater started (no GUI).")
// Start a separate goroutine to periodically update the progress in update_state.ini
go func() {
for {
// Get current progress and task from spm
progress, task := spm.GetProgress()
// Determine if the updater is in "Copying files to install directory" stage
isUpdating := task == "Copying files to install directory"
// Write the state to the update_state.ini file
WriteState(isUpdating, int32(progress), task)
// Sleep for 1 second before updating the progress again
time.Sleep(1 * time.Second)
}
}()
// Main loop for periodic AutoDownloadUpdates and AutoInstallUpdates
for {
// Run AutoDownloadUpdates every 10 seconds
err := spm.AutoDownloadUpdates()
if err != nil {
log.Printf("AutoDownloadUpdates failed: %v\n", err)
WriteState(false, 0, fmt.Sprintf("Update failed: %v", err))
time.Sleep(10 * time.Second)
continue
}
// Check if the browser is running
if IsBrowserRunning() {
log.Println("Browser is running. Waiting before installing updates.")
time.Sleep(10 * time.Second)
continue
}
// If the browser is not running, run AutoInstallUpdates
log.Println("\nBrowser is not running. Starting installation of updates.")
err = spm.AutoInstallUpdates()
if err != nil {
log.Printf("AutoInstallUpdates failed: %v\n", err)
WriteState(false, 0, fmt.Sprintf("Installation failed: %v", err))
time.Sleep(10 * time.Second)
continue
}
// Write to update_state.ini indicating updates are complete
WriteState(false, 100, "Complete")
log.Println("Updates installed successfully. Waiting before next check.")
// Wait 10 seconds before checking again
time.Sleep(10 * time.Second)
}
}
// -------------------------------------------------------------------
// MAIN
// -------------------------------------------------------------------
func main() {
if len(os.Args) > 1 && os.Args[1] == "--update-service" {
isBackgroundMode = true
runBackgroundUpdater()
return
}
log.Println("Launcher started (foreground mode).")
sf := ReadState()
if sf.IsUpdating == true {
log.Println("Stage=Installing => show update window.")
ShowUpdateWindow()
} else {
LaunchBrowser()
}
fmt.Println("Exiting launcher.")
}