added --silent and register values in windows registry keys
This commit is contained in:
parent
48473f98c5
commit
ca57775f8f
7 changed files with 402 additions and 165 deletions
|
@ -10,7 +10,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// DecompressPackage determines the package format and decompresses it
|
||||
// DecompressPackage now passes UpdateProgress to decompressTarGz.
|
||||
func DecompressPackage(downloadDir, packageName, arch, osName, pkgType, release, version string) (string, error) {
|
||||
// 1) Construct the .tar.gz name
|
||||
expectedFileName := fmt.Sprintf(
|
||||
|
@ -35,7 +35,7 @@ func DecompressPackage(downloadDir, packageName, arch, osName, pkgType, release,
|
|||
}
|
||||
|
||||
// 3) Decompress everything into `decompressDir`
|
||||
if err := decompressTarGz(packagePath, decompressDir); err != nil {
|
||||
if err := decompressTarGz(packagePath, decompressDir, UpdateProgress); err != nil {
|
||||
return "", fmt.Errorf("failed to decompress: %w", err)
|
||||
}
|
||||
|
||||
|
@ -43,14 +43,28 @@ func DecompressPackage(downloadDir, packageName, arch, osName, pkgType, release,
|
|||
return decompressDir, nil
|
||||
}
|
||||
|
||||
func decompressTarGz(srcFile, destDir string) error {
|
||||
// decompressTarGz takes an additional updateProgress callback to report progress.
|
||||
func decompressTarGz(srcFile, destDir string, updateProgress func(int, string)) error {
|
||||
f, err := os.Open(srcFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
gzr, err := gzip.NewReader(f)
|
||||
fileInfo, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
totalSize := fileInfo.Size()
|
||||
|
||||
// Wrap the file reader so we can track progress.
|
||||
progressReader := &ProgressReader{
|
||||
Reader: f,
|
||||
Total: totalSize,
|
||||
Callback: updateProgress,
|
||||
}
|
||||
|
||||
gzr, err := gzip.NewReader(progressReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -73,6 +87,11 @@ func decompressTarGz(srcFile, destDir string) error {
|
|||
return err
|
||||
}
|
||||
case tar.TypeReg:
|
||||
outPath := filepath.Join(destDir, header.Name)
|
||||
// Ensure the parent directory exists
|
||||
if err := os.MkdirAll(filepath.Dir(outPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
outFile, err := os.Create(outPath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -83,8 +102,38 @@ func decompressTarGz(srcFile, destDir string) error {
|
|||
return err
|
||||
}
|
||||
default:
|
||||
// huh
|
||||
// ignoring other types for now
|
||||
}
|
||||
|
||||
// Update progress after extracting each file.
|
||||
if updateProgress != nil {
|
||||
percent := int((progressReader.BytesRead * 100) / totalSize)
|
||||
updateProgress(percent, fmt.Sprintf("Extracted: %s", header.Name))
|
||||
}
|
||||
}
|
||||
|
||||
// Final update: extraction complete.
|
||||
if updateProgress != nil {
|
||||
updateProgress(100, "Extraction complete")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProgressReader wraps an io.Reader to count bytes and update progress.
|
||||
type ProgressReader struct {
|
||||
io.Reader
|
||||
Total int64
|
||||
BytesRead int64
|
||||
Callback func(int, string)
|
||||
}
|
||||
|
||||
func (pr *ProgressReader) Read(p []byte) (int, error) {
|
||||
n, err := pr.Reader.Read(p)
|
||||
pr.BytesRead += int64(n)
|
||||
if pr.Callback != nil && pr.Total > 0 {
|
||||
percent := int((pr.BytesRead * 100) / pr.Total)
|
||||
pr.Callback(percent, "Decompressing...")
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
|
17
spm/register_unix.go
Normal file
17
spm/register_unix.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
// run_default.go
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package spm
|
||||
|
||||
import "fmt"
|
||||
|
||||
// RegisterApp is not supported on non-Windows platforms.
|
||||
func RegisterApp() error {
|
||||
return fmt.Errorf("RegisterApp is only available on Windows")
|
||||
}
|
||||
|
||||
// UnregisterApp is not supported on non-Windows platforms.
|
||||
func UnregisterApp() error {
|
||||
return fmt.Errorf("UnregisterApp is only available on Windows")
|
||||
}
|
135
spm/register_win.go
Normal file
135
spm/register_win.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
// run_windows.go
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package spm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
// RegisterApp writes the necessary registry keys, making it appear as offically installed app
|
||||
func RegisterApp() error {
|
||||
exePath, err := GetInstallDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get install directory: %w", err)
|
||||
}
|
||||
|
||||
// 1. Create Uninstall/Modify entry
|
||||
uninstallKeyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\SpitfireBrowser`
|
||||
uk, _, err := registry.CreateKey(registry.LOCAL_MACHINE, uninstallKeyPath, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create uninstall key: %w", err)
|
||||
}
|
||||
defer uk.Close()
|
||||
|
||||
if err := uk.SetStringValue("DisplayName", "Spitfire"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uk.SetStringValue("UninstallString", exePath+" --uninstall"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uk.SetStringValue("ModifyPath", exePath+" --modify"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := uk.SetStringValue("DisplayIcon", exePath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Register as a browser for default apps
|
||||
clientKeyPath := `Software\Clients\StartMenuInternet\SpitfireBrowser`
|
||||
ck, _, err := registry.CreateKey(registry.LOCAL_MACHINE, clientKeyPath, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create client key: %w", err)
|
||||
}
|
||||
defer ck.Close()
|
||||
|
||||
if err := ck.SetStringValue("", "Spitfire"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create Capabilities subkey
|
||||
capabilitiesKeyPath := clientKeyPath + `\Capabilities`
|
||||
capk, _, err := registry.CreateKey(registry.LOCAL_MACHINE, capabilitiesKeyPath, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create capabilities key: %w", err)
|
||||
}
|
||||
defer capk.Close()
|
||||
|
||||
if err := capk.SetStringValue("ApplicationName", "Spitfire"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := capk.SetStringValue("ApplicationDescription", "A custom browser"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set file associations
|
||||
assocKeyPath := capabilitiesKeyPath + `\FileAssociations`
|
||||
ak, _, err := registry.CreateKey(registry.LOCAL_MACHINE, assocKeyPath, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file associations key: %w", err)
|
||||
}
|
||||
defer ak.Close()
|
||||
|
||||
associations := map[string]string{
|
||||
".html": "SpitfireBrowserHTML",
|
||||
"HTTP": "SpitfireBrowserHTML",
|
||||
"HTTPS": "SpitfireBrowserHTML",
|
||||
}
|
||||
for ext, progID := range associations {
|
||||
if err := ak.SetStringValue(ext, progID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnregisterApp removes the registry entries created by registerApp.
|
||||
func UnregisterApp() error {
|
||||
// Remove the Uninstall/Modify entry.
|
||||
uninstallKeyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\SpitfireBrowser`
|
||||
if err := deleteRegistryTree(registry.LOCAL_MACHINE, uninstallKeyPath); err != nil {
|
||||
return fmt.Errorf("failed to delete uninstall key: %w", err)
|
||||
}
|
||||
|
||||
// Remove the browser registration entry.
|
||||
clientKeyPath := `Software\Clients\StartMenuInternet\SpitfireBrowser`
|
||||
if err := deleteRegistryTree(registry.LOCAL_MACHINE, clientKeyPath); err != nil {
|
||||
return fmt.Errorf("failed to delete client key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteRegistryTree recursively deletes a registry key and all its subkeys.
|
||||
func deleteRegistryTree(root registry.Key, path string) error {
|
||||
// Open the key with ALL_ACCESS permissions.
|
||||
key, err := registry.OpenKey(root, path, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
// If the key does not exist, there's nothing to do.
|
||||
if err == registry.ErrNotExist {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// Read the names of all subkeys.
|
||||
subKeys, err := key.ReadSubKeyNames(-1)
|
||||
key.Close() // Close the key so it can be deleted later.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Recursively delete each subkey.
|
||||
for _, subKey := range subKeys {
|
||||
subKeyPath := path + `\` + subKey
|
||||
if err := deleteRegistryTree(root, subKeyPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, delete the (now empty) key.
|
||||
return registry.DeleteKey(root, path)
|
||||
}
|
74
spm/run_unix.go
Normal file
74
spm/run_unix.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
// run_unix.go
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package spm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Run locates and starts the installed Spitfire browser without waiting for it to exit.
|
||||
func Run() error {
|
||||
installDir, err := GetInstallDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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")
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
// RunAndWait locates and starts the installed Spitfire browser and waits for it to exit.
|
||||
func RunAndWait() error {
|
||||
installDir, err := GetInstallDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get install directory: %w", err)
|
||||
}
|
||||
|
||||
// Construct the browser executable path
|
||||
exePath := filepath.Join(installDir, "browser", "spitfire")
|
||||
if _, err := os.Stat(exePath); err != nil {
|
||||
return fmt.Errorf("browser executable not found at %s: %w", exePath, err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(exePath)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// Start the process in a new process group
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
fmt.Printf("Starting browser: %s\n", exePath)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start browser: %w", err)
|
||||
}
|
||||
|
||||
// Print PID and PGID for debugging
|
||||
pgid, err := syscall.Getpgid(cmd.Process.Pid)
|
||||
if err == nil {
|
||||
fmt.Printf("Browser process started with PID %d (PGID %d)\n", cmd.Process.Pid, pgid)
|
||||
} else {
|
||||
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 successfully.")
|
||||
return nil
|
||||
}
|
|
@ -1,71 +1,68 @@
|
|||
package spm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Run locates and starts the installed Spitfire browser without waiting for it to exit.
|
||||
func Run() error {
|
||||
installDir, err := GetInstallDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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")
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
// RunAndWait locates and starts the installed Spitfire browser and waits for it to exit.
|
||||
func RunAndWait() error {
|
||||
installDir, err := GetInstallDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get install directory: %w", err)
|
||||
}
|
||||
|
||||
// Construct the browser executable path
|
||||
exePath := filepath.Join(installDir, "browser", "spitfire.exe")
|
||||
if runtime.GOOS != "windows" {
|
||||
exePath = filepath.Join(installDir, "browser", "spitfire")
|
||||
}
|
||||
|
||||
// Check if the browser executable exists
|
||||
if _, err := os.Stat(exePath); err != nil {
|
||||
return fmt.Errorf("browser executable not found at %s: %w", exePath, err)
|
||||
}
|
||||
|
||||
// Create the command
|
||||
cmd := exec.Command(exePath)
|
||||
|
||||
// Attach standard output and error for debugging
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// Start the browser process
|
||||
fmt.Printf("Starting browser: %s\n", exePath)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start browser: %w", err)
|
||||
}
|
||||
|
||||
// Print the PID for debugging
|
||||
fmt.Printf("Browser process started with PID %d\n", cmd.Process.Pid)
|
||||
|
||||
// Wait for the process to exit
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("browser exited with error: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Browser exited successfully.")
|
||||
return nil
|
||||
}
|
||||
// run_windows.go
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package spm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Run locates and starts the installed Spitfire browser without waiting for it to exit.
|
||||
func Run() error {
|
||||
installDir, err := GetInstallDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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")
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
// RunAndWait locates and starts the installed Spitfire browser and waits for it to exit.
|
||||
func RunAndWait() error {
|
||||
installDir, err := GetInstallDir()
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
cmd := exec.Command(exePath)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// Use CREATE_NEW_PROCESS_GROUP flag for Windows
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
|
||||
}
|
||||
|
||||
fmt.Printf("Starting browser: %s\n", exePath)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start browser: %w", err)
|
||||
}
|
||||
|
||||
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 successfully.")
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue