diff --git a/README.md b/README.md index 63f036c..b65fd01 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,9 @@ func main() { } // -- Download -- - // spm.DownloadSpecified(specs) downloads specified packages to temp dir and decompresses them, making them ready for install by running "spm.InstallUpdates()". + // spm.AutoDownloadSpecified(specs) downloads specified packages to temp dir and decompresses them, making them ready for install by running "spm.AutoInstallUpdates()". fmt.Println("Starting download and decompression...") - if err := spm.DownloadSpecified(specs); err != nil { + if err := spm.AutoDownloadSpecified(specs); err != nil { fmt.Println("Error downloading packages:", err) return } @@ -57,15 +57,15 @@ func main() { fmt.Println("Download complete. Proceeding with installation...") // -- Install -- - // Install and Download are separate as you cannot replace running binaries on Windows. So the final move to the correct folder is done by "spm.InstallUpdates()". - if err := spm.InstallUpdates(); err != nil { + // Install and Download are separate as you cannot replace running binaries on Windows. So the final move to the correct folder is done by "spm.AutoInstallUpdates()". + if err := spm.AutoInstallUpdates(); err != nil { fmt.Println("Error during installation:", err) return } // -- Register -- - // spm.RegisterApp() is primarily used to modify the Windows registry so it recognizes Spitfire Browser as an installed program. - // You shouldn’t need to run it more than once during installation. Also this function requires administrative privileges on Windows to work correctly. +// spm.RegisterApp() is primarily used to modify the Windows registry so it recognizes Spitfire Browser as an installed program. +// You shouldn’t need to run it more than once during installation. Also this function requires administrative privileges on Windows to work correctly. if err := spm.RegisterApp(); err != nil { fmt.Println("Error registering app:", err) return @@ -78,6 +78,7 @@ func main() { spm.Run() } ``` +*Functions, and specifically their names, are subject to change as I really don't like "AutoDownloadSpecified," but I don't want to make it "DownloadSpecified" yet, as there are still many functions in SPM used for manual downloads, installs, etc.*
License diff --git a/appindex.go b/appindex.go index 848d59e..14a77be 100644 --- a/appindex.go +++ b/appindex.go @@ -6,46 +6,14 @@ import ( "io" "net/http" "os" - "path/filepath" - "sort" "strings" - - "gopkg.in/ini.v1" ) -// AppIndexEntry represents a single entry in an app index. -type AppIndexEntry struct { - Name string - Version string - Release string // "nightly" / "stable" / etc. - Arch string // e.g. "amd64", "386" - OS string // e.g. "windows", "linux" - Type string // "browser", "addon", "theme", etc. - DownloadURL string -} +const appIndexURL = "https://downloads.sourceforge.net/project/spitfire-browser/APPINDEX" -// RemoteIndex represents a remote APPINDEX repository. -type RemoteIndex struct { - Name string `json:"name"` - Link string `json:"link"` -} - -var ( - // defaultRemoteIndexes holds the default remote index. - defaultRemoteIndexes = []RemoteIndex{ - { - Name: "default", - Link: "https://downloads.sourceforge.net/project/spitfire-browser/APPINDEX", - }, - } - // remoteIndexes holds the current remote indexes in use. - remoteIndexes = defaultRemoteIndexes -) - -// downloadAppIndex downloads an APPINDEX from the given URL and writes it to dest. -func downloadAppIndex(url, dest string) error { +func DownloadAppIndex(dest string) error { UpdateProgress(0, "Downloading APPINDEX") - resp, err := http.Get(url) + resp, err := http.Get(appIndexURL) if err != nil { return err } @@ -84,10 +52,25 @@ func downloadAppIndex(url, dest string) error { return nil } -// parseAppIndexFromReader parses an APPINDEX from any io.Reader. -func parseAppIndexFromReader(r io.Reader) ([]AppIndexEntry, error) { +type AppIndexEntry struct { + Name string + Version string + Release string // "nightly" / "stable" / etc. + Arch string // e.g. "amd64", "386" + OS string // e.g. "windows", "linux" + Type string // "browser", "addon", "theme", etc. + DownloadURL string +} + +func ParseAppIndex(filePath string) ([]AppIndexEntry, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer file.Close() + var entries []AppIndexEntry - scanner := bufio.NewScanner(r) + scanner := bufio.NewScanner(file) entry := AppIndexEntry{} for scanner.Scan() { @@ -129,222 +112,12 @@ func parseAppIndexFromReader(r io.Reader) ([]AppIndexEntry, error) { entries = append(entries, entry) } - return entries, scanner.Err() -} - -// parseAppIndex reads the APPINDEX file at filePath and parses its contents. -func parseAppIndex(filePath string) ([]AppIndexEntry, error) { - file, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer file.Close() - - entries, err := parseAppIndexFromReader(file) - if err != nil { - return nil, err - } - - fmt.Printf("[INFO] Total parsed entries from %s: %d\n", filePath, len(entries)) + // Log all parsed entries + fmt.Printf("[INFO] Total parsed entries: %d\n", len(entries)) for _, e := range entries { fmt.Printf(" - Name: %s, Release: %s, Type: %s, OS: %s, Arch: %s, Version: %s, URL: %s\n", e.Name, e.Release, e.Type, e.OS, e.Arch, e.Version, e.DownloadURL) } - return entries, nil -} - -// saveIndex saves the current list of remote indexes to an INI file located in -// the spm directory under installDir, but only if it's different from the default. -func saveIndex() error { - // Only save if remoteIndexes differs from defaultRemoteIndexes. - if len(remoteIndexes) == len(defaultRemoteIndexes) { - same := true - for i, ri := range remoteIndexes { - if ri != defaultRemoteIndexes[i] { - same = false - break - } - } - if same { - return nil - } - } - - installDir, err := GetInstallDir() - if err != nil { - return err - } - spmDir := filepath.Join(installDir, "spm") - if err := os.MkdirAll(spmDir, 0755); err != nil { - return err - } - filePath := filepath.Join(spmDir, "sources.ini") - - cfg := ini.Empty() - sec, err := cfg.NewSection("RemoteIndexes") - if err != nil { - return err - } - // Save each remote index as a key/value pair. - for _, ri := range remoteIndexes { - if _, err := sec.NewKey(ri.Name, ri.Link); err != nil { - return err - } - } - - return cfg.SaveTo(filePath) -} - -// loadIndex loads the list of remote indexes from an INI file located in -// the spm directory under installDir. If the file is missing, it sets remoteIndexes to the default. -func loadIndex() error { - installDir, err := GetInstallDir() - if err != nil { - return err - } - spmDir := filepath.Join(installDir, "spm") - filePath := filepath.Join(spmDir, "sources.ini") - cfg, err := ini.Load(filePath) - if err != nil { - // If file is missing or can't be loaded, use the default. - remoteIndexes = defaultRemoteIndexes - return nil - } - sec := cfg.Section("RemoteIndexes") - var loaded []RemoteIndex - for _, key := range sec.Keys() { - loaded = append(loaded, RemoteIndex{ - Name: key.Name(), - Link: key.Value(), - }) - } - remoteIndexes = loaded - return nil -} - -// UpdateIndex downloads fresh APPINDEX files from all remote sources and saves them -// into the temp directory. If the app is registered, it loads the remote indexes -// from the INI file (or uses the default if not available) and saves them after updating. -func UpdateIndex() error { - tempDir := GetTempDir() - var sources []RemoteIndex - if IsRegistered() { - // Try to load persisted remote indexes. - if err := loadIndex(); err != nil { - // If loading fails, fall back to the default remote indexes. - sources = defaultRemoteIndexes - } else { - sources = remoteIndexes - } - } else { - // Not registered: use default remote indexes. - sources = defaultRemoteIndexes - } - - // Download each APPINDEX file. - for _, ri := range sources { - localPath := filepath.Join(tempDir, fmt.Sprintf("appindex_%s.txt", ri.Name)) - if err := downloadAppIndex(ri.Link, localPath); err != nil { - return fmt.Errorf("[WARN] AppIndex: failed downloading %s: %v", ri.Link, err) - } - } - - // If registered, save the current remote indexes. - if IsRegistered() { - if err := saveIndex(); err != nil { - return fmt.Errorf("[WARN] AppIndex: failed saving indexes: %v", err) - } - } - return nil -} - -// GetIndex parses APPINDEX data from local files in the temp directory. -// If a file is missing, it downloads the corresponding APPINDEX first. -// If the app is registered, it loads remote indexes from the INI file. -// Otherwise, it uses the default remote index. -func GetIndex() ([]AppIndexEntry, error) { - var allEntries []AppIndexEntry - tempDir := GetTempDir() - var sources []RemoteIndex - if IsRegistered() { - if err := loadIndex(); err != nil { - sources = defaultRemoteIndexes - } else { - sources = remoteIndexes - } - } else { - sources = defaultRemoteIndexes - } - - // For each remote source, ensure the APPINDEX file exists (downloading if needed), - // then parse its contents. - for _, ri := range sources { - localPath := filepath.Join(tempDir, fmt.Sprintf("appindex_%s.txt", ri.Name)) - if _, err := os.Stat(localPath); os.IsNotExist(err) { - if err := downloadAppIndex(ri.Link, localPath); err != nil { - return nil, fmt.Errorf("[WARN] AppIndex: failed downloading %s: %v", ri.Link, err) - } - } - entries, err := parseAppIndex(localPath) - if err != nil { - return nil, fmt.Errorf("[WARN] AppIndex: failed parsing %s: %v", localPath, err) - } - allEntries = append(allEntries, entries...) - } - return allEntries, nil -} - -// AddIndex adds a new remote index (name and link) into the list, -// sorts the list by name, and if the app is registered, saves the updated list. -func AddIndex(name, link string) error { - // If registered, load current indexes first. - if IsRegistered() { - if err := loadIndex(); err != nil { - return fmt.Errorf("[WARN] AppIndex: failed loading indexes: %w", err) - } - } - - ri := RemoteIndex{ - Name: name, - Link: link, - } - remoteIndexes = append(remoteIndexes, ri) - sort.Slice(remoteIndexes, func(i, j int) bool { - return remoteIndexes[i].Name < remoteIndexes[j].Name - }) - - // If registered, persist the changes. - if IsRegistered() { - if err := saveIndex(); err != nil { - return fmt.Errorf("[WARN] AppIndex: failed saving indexes: %w", err) - } - } - return nil -} - -// RemoveIndex removes any remote index with the given name from the list, -// and if the app is registered, saves the updated list. -func RemoveIndex(name string) error { - // If registered, load current indexes first. - if IsRegistered() { - if err := loadIndex(); err != nil { - return fmt.Errorf("[WARN] AppIndex: failed loading indexes: %w", err) - } - } - - var updated []RemoteIndex - for _, ri := range remoteIndexes { - if ri.Name != name { - updated = append(updated, ri) - } - } - remoteIndexes = updated - - // If registered, persist the changes. - if IsRegistered() { - if err := saveIndex(); err != nil { - return fmt.Errorf("[WARN] AppIndex: failed saving indexes: %w", err) - } - } - return nil + + return entries, scanner.Err() } diff --git a/auto.go b/auto.go index d544a44..92e574a 100644 --- a/auto.go +++ b/auto.go @@ -2,6 +2,7 @@ package spm import ( "fmt" + "os" "path/filepath" ) @@ -9,19 +10,22 @@ import ( // but not yet moved to the final install location, cuz Windows has this stupid file locking mechanism var pendingUpdates []AppIndexEntry -// DownloadUpdates downloads the APPINDEX file, parses it, compares against +// AutoDownloadUpdates downloads the APPINDEX file, parses it, compares against // currently installed packages, and if it finds a newer version, downloads -// and decompresses it into a temporary folder. The result is stored in pendingUpdates, so it can be used by InstallUpdates(). -func DownloadUpdates() error { +// and decompresses it into a temporary folder. The result is stored in pendingUpdates, so it can be used by AutoInstallUpdates(). +func AutoDownloadUpdates() error { // 1) Download the APPINDEX file to a temporary location - err := UpdateIndex() + appIndexPath := filepath.Join(os.TempDir(), "APPINDEX") + fmt.Println("[INFO] Starting APPINDEX download to:", appIndexPath) + err := DownloadAppIndex(appIndexPath) if err != nil { return fmt.Errorf("[ERROR] Failed to download APPINDEX: %w", err) } fmt.Println("[INFO] APPINDEX downloaded successfully") // 2) Parse the APPINDEX file - entries, err := GetIndex() + fmt.Println("[INFO] Parsing APPINDEX file:", appIndexPath) + entries, err := ParseAppIndex(appIndexPath) if err != nil { return fmt.Errorf("[ERROR] Failed to parse APPINDEX: %w", err) } @@ -80,7 +84,7 @@ func DownloadUpdates() error { downloadDir := GetTempDir() fmt.Printf("[INFO] Downloading package '%s' to temporary folder: %s\n", matchingEntry.Name, downloadDir) - err = DownloadPackageFromAppIndex(matchingEntry.Name, matchingEntry.Release, matchingEntry.Type, downloadDir) + err = DownloadPackageFromAppIndex(appIndexPath, matchingEntry.Name, matchingEntry.Release, matchingEntry.Type, downloadDir) if err != nil { return fmt.Errorf("[ERROR] Failed to download package '%s': %w", matchingEntry.Name, err) } @@ -95,7 +99,7 @@ func DownloadUpdates() error { } fmt.Printf("[INFO] Package '%s' decompressed successfully to: %s\n", matchingEntry.Name, tempDir) - // 7) Store in pendingUpdates so that InstallUpdates can finish the job + // 7) Store in pendingUpdates so that AutoInstallUpdates can finish the job fmt.Printf("[INFO] Adding '%s' to pending updates\n", matchingEntry.Name) pendingUpdates = append(pendingUpdates, AppIndexEntry{ Name: matchingEntry.Name, @@ -107,13 +111,13 @@ func DownloadUpdates() error { }) } - fmt.Println("[INFO] DownloadUpdates completed successfully") + fmt.Println("[INFO] AutoDownloadUpdates completed successfully") return nil } -// InstallUpdates installs any packages that were downloaded and decompressed by DownloadUpdates. +// AutoInstallUpdates installs any packages that were downloaded and decompressed by AutoDownloadUpdates. // It moves files from their temp directories to the final location and updates installed.ini. -func InstallUpdates() error { +func AutoInstallUpdates() error { installDir, err := GetInstallDir() if err != nil { return err @@ -142,7 +146,7 @@ func InstallUpdates() error { } // 5) Finalize - err = finalizeInstall(entry.Name, entry.Release, entry.Version, entry.Arch, entry.OS) + err = FinalizeInstall(entry.Name, entry.Release, entry.Version, entry.Arch, entry.OS) if err != nil { return fmt.Errorf("failed to finalize install for %s: %w", entry.Name, err) } @@ -152,15 +156,18 @@ func InstallUpdates() error { return nil } -func DownloadSpecified(specs []AppIndexEntry) error { +func AutoDownloadSpecified(specs []AppIndexEntry) error { // 1) Download the APPINDEX file to a temporary location - if err := UpdateIndex(); err != nil { + appIndexPath := filepath.Join(os.TempDir(), "APPINDEX") + fmt.Println("[INFO] Starting APPINDEX download to:", appIndexPath) + if err := DownloadAppIndex(appIndexPath); err != nil { return fmt.Errorf("[ERROR] Failed to download APPINDEX: %w", err) } fmt.Println("[INFO] APPINDEX downloaded successfully") // 2) Parse the APPINDEX file - entries, err := GetIndex() + fmt.Println("[INFO] Parsing APPINDEX file:", appIndexPath) + entries, err := ParseAppIndex(appIndexPath) if err != nil { return fmt.Errorf("[ERROR] Failed to parse APPINDEX: %w", err) } @@ -196,27 +203,29 @@ func DownloadSpecified(specs []AppIndexEntry) error { } fmt.Printf("[INFO] Found matching APPINDEX entry: %+v\n", *matchingEntry) - updateNeeded, err := IsUpdateNeeded( - installDir, - matchingEntry.Name, - matchingEntry.Release, - matchingEntry.Version, - matchingEntry.Arch, - matchingEntry.OS, - ) - if err != nil { - return fmt.Errorf("[ERROR] Failed to check if update is needed for %s: %w", matchingEntry.Name, err) - } + // // Check if an update is needed + // updateNeeded, err := IsUpdateNeeded( + // installDir, + // matchingEntry.Name, + // matchingEntry.Release, + // matchingEntry.Version, + // matchingEntry.Arch, + // matchingEntry.OS, + // ) + // if err != nil { + // return fmt.Errorf("[ERROR] Failed to check if update is needed for %s: %w", matchingEntry.Name, err) + // } - if !updateNeeded { - fmt.Printf("[INFO] No update needed for package '%s'\n", matchingEntry.Name) - continue - } + // if !updateNeeded { + // fmt.Printf("[INFO] No update needed for package '%s'\n", matchingEntry.Name) + // continue + // } // 5) Download the package downloadDir := GetTempDir() fmt.Printf("[INFO] Downloading package '%s' to temporary folder: %s\n", matchingEntry.Name, downloadDir) if err := DownloadPackageFromAppIndex( + appIndexPath, matchingEntry.Name, matchingEntry.Release, matchingEntry.Type, @@ -242,7 +251,7 @@ func DownloadSpecified(specs []AppIndexEntry) error { } fmt.Printf("[INFO] Package '%s' decompressed successfully to: %s\n", matchingEntry.Name, tempDir) - // Add to pendingUpdates for InstallUpdates + // 7) Store in pendingUpdates for AutoInstallUpdates fmt.Printf("[INFO] Adding '%s' to pending updates\n", matchingEntry.Name) pendingUpdates = append(pendingUpdates, *matchingEntry) } diff --git a/decompress.go b/decompress.go index 00003e2..7f7a207 100644 --- a/decompress.go +++ b/decompress.go @@ -108,11 +108,7 @@ func decompressTarGz(srcFile, destDir string, updateProgress func(int, string)) // Update progress after extracting each file. if updateProgress != nil { percent := int((progressReader.BytesRead * 100) / totalSize) - name := header.Name - if len(name) > 50 { - name = name[len(name)-50:] - } - updateProgress(percent, fmt.Sprintf("Extracted: %s", name)) + updateProgress(percent, fmt.Sprintf("Extracted: %s", header.Name)) } } diff --git a/download.go b/download.go index c6464f2..30d239c 100644 --- a/download.go +++ b/download.go @@ -11,9 +11,9 @@ import ( ) // DownloadPackageFromAppIndex selects and downloads the correct package from the APPINDEX. -func DownloadPackageFromAppIndex(packageName string, release string, pkgType string, destDir string) error { +func DownloadPackageFromAppIndex(appIndexPath string, packageName string, release string, pkgType string, destDir string) error { // Parse the APPINDEX - entries, err := GetIndex() + entries, err := ParseAppIndex(appIndexPath) if err != nil { return fmt.Errorf("failed to parse APPINDEX: %w", err) } @@ -99,8 +99,8 @@ func DownloadPackageFromAppIndex(packageName string, release string, pkgType str expectedFilePath := filepath.Join(destDir, expectedFileName) - // I dont know why is this happening, I dont want to know but sometimes some process is helding up the downloaded files so thats why it retries here - maxRetries := 10 + // I dont know why is this happening, I dont want to know but sometimes some process is helding up the donwloaded files so thats why it retries here + maxRetries := 5 for i := 0; i < maxRetries; i++ { err = os.Rename(downloadedFilePath, expectedFilePath) if err == nil { @@ -115,7 +115,7 @@ func DownloadPackageFromAppIndex(packageName string, release string, pkgType str f.Close() if i < maxRetries-1 { - time.Sleep(250 * time.Millisecond) // Wait before retrying + time.Sleep(500 * time.Millisecond) // Wait before retrying } } diff --git a/go.mod b/go.mod index f4c1524..8b67881 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,7 @@ module weforge.xyz/Spitfire/SPM -go 1.23.0 +go 1.21 -toolchain go1.24.1 - -require ( - golang.org/x/sys v0.31.0 - gopkg.in/ini.v1 v1.67.0 -) +require gopkg.in/ini.v1 v1.67.0 require github.com/stretchr/testify v1.10.0 // indirect diff --git a/go.sum b/go.sum index bd54818..be248fb 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/install.go b/install.go index a8d8521..9fbf274 100644 --- a/install.go +++ b/install.go @@ -287,8 +287,8 @@ func copyFile(src, dst string) error { return os.Chmod(dst, info.Mode()) } -// finalizeInstall finalizes the installation by updating installed.ini. -func finalizeInstall(packageName, release, version, arch, osName string) error { +// FinalizeInstall finalizes the installation by updating installed.ini. +func FinalizeInstall(packageName, release, version, arch, osName string) error { installDir, err := GetInstallDir() if err != nil { return err diff --git a/register_unix.go b/register_unix.go index f9e35c9..6568edd 100644 --- a/register_unix.go +++ b/register_unix.go @@ -4,34 +4,14 @@ package spm -import ( - "fmt" - "os" - "path/filepath" -) +import "fmt" // RegisterApp is not supported on non-Windows platforms. func RegisterApp() error { - return fmt.Errorf("[WARN] RegisterApp() is only available on Windows") + return fmt.Errorf("RegisterApp is only available on Windows") } // UnregisterApp is not supported on non-Windows platforms. func UnregisterApp() error { - return fmt.Errorf("[WARN] UnregisterApp() is only available on Windows") -} - -// IsRegistered returns true if the application is detected as installed. -// On Linux, we assume it is installed if the main executable exists in the install directory. -func IsRegistered() bool { - installDir, err := GetInstallDir() - if err != nil { - return false - } - - // Assume the executable is named "spitfire" and is located in installDir. - exePath := filepath.Join(installDir, "browser", "spitfire") - if _, err := os.Stat(exePath); err == nil { - return true - } - return false + return fmt.Errorf("UnregisterApp is only available on Windows") } diff --git a/register_win.go b/register_win.go index 206f0ae..b2cf47f 100644 --- a/register_win.go +++ b/register_win.go @@ -10,7 +10,7 @@ import ( "golang.org/x/sys/windows/registry" ) -// RegisterApp writes the necessary registry keys, making it appear as officially installed app +// RegisterApp writes the necessary registry keys, making it appear as offically installed app func RegisterApp() error { exePath, err := GetInstallDir() if err != nil { @@ -133,15 +133,3 @@ func deleteRegistryTree(root registry.Key, path string) error { // Finally, delete the (now empty) key. return registry.DeleteKey(root, path) } - -// IsRegistered returns true if the application is registered (installed) in the registry. -func IsRegistered() bool { - // Try to open the uninstall key with read-only access. - key, err := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Microsoft\Windows\CurrentVersion\Uninstall\SpitfireBrowser`, registry.READ) - if err != nil { - // If the key cannot be opened, assume the app is not registered. - return false - } - defer key.Close() - return true -} diff --git a/run_unix.go b/run_unix.go index aa580b2..cc82b79 100644 --- a/run_unix.go +++ b/run_unix.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "syscall" ) @@ -20,6 +21,9 @@ func Run() error { } exePath := filepath.Join(installDir, "browser", "spitfire.exe") + if runtime.GOOS != "windows" { + exePath = filepath.Join(installDir, "browser", "spitfire") + } cmd := exec.Command(exePath) cmd.Dir = filepath.Join(installDir, "browser") diff --git a/run_win.go b/run_win.go index 706b6d4..d1f2b38 100644 --- a/run_win.go +++ b/run_win.go @@ -9,10 +9,8 @@ import ( "os" "os/exec" "path/filepath" - "strings" - "time" - - "golang.org/x/sys/windows" + "runtime" + "syscall" ) // Run locates and starts the installed Spitfire browser without waiting for it to exit. @@ -23,6 +21,9 @@ func Run() error { } exePath := filepath.Join(installDir, "browser", "spitfire.exe") + if runtime.GOOS != "windows" { + exePath = filepath.Join(installDir, "browser", "spitfire") + } cmd := exec.Command(exePath) cmd.Dir = filepath.Join(installDir, "browser") @@ -36,6 +37,7 @@ func RunAndWait() error { return fmt.Errorf("failed to get install directory: %w", err) } + // Construct the browser executable path exePath := filepath.Join(installDir, "browser", "spitfire.exe") if _, err := os.Stat(exePath); err != nil { return fmt.Errorf("browser executable not found at %s: %w", exePath, err) @@ -44,29 +46,23 @@ func RunAndWait() error { cmd := exec.Command(exePath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - cmd.Dir = filepath.Join(installDir, "browser") - // Create job object starting the process - job, err := windows.CreateJobObject(nil, nil) - if err != nil { - return fmt.Errorf("failed to create job object: %w", err) + // Use CREATE_NEW_PROCESS_GROUP flag for Windows + cmd.SysProcAttr = &syscall.SysProcAttr{ + CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, } - defer windows.CloseHandle(job) fmt.Printf("Starting browser: %s\n", exePath) if err := cmd.Start(); err != nil { return fmt.Errorf("failed to start browser: %w", err) } - for { - cmd := exec.Command("tasklist", "/FI", "IMAGENAME eq spitfire.exe") - output, _ := cmd.Output() - if !strings.Contains(string(output), "spitfire.exe") { - break - } - time.Sleep(1 * time.Second) + fmt.Printf("Browser process started with PID %d\n", cmd.Process.Pid) + + if err := cmd.Wait(); err != nil { + return fmt.Errorf("browser exited with error: %w", err) } - fmt.Println("Browser exited.") + fmt.Println("Browser exited successfully.") return nil }