mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-17 10:31:45 +02:00
113 lines
3.5 KiB
Go
113 lines
3.5 KiB
Go
//go:build darwin
|
|
|
|
package updatemanager
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/user"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
const (
|
|
pkgDownloadURL = "https://github.com/netbirdio/netbird/releases/download/v%version/netbird_%version_darwin_%arch.pkg"
|
|
)
|
|
|
|
func (u *UpdateManager) triggerUpdate(ctx context.Context, targetVersion string) error {
|
|
cmd := exec.CommandContext(ctx, "pkgutil", "--pkg-info", "io.netbird.client")
|
|
outBytes, err := cmd.Output()
|
|
if err != nil && cmd.ProcessState.ExitCode() == 1 {
|
|
// Not installed using pkg file, thus installed using Homebrew
|
|
|
|
return u.updateHomeBrew(ctx)
|
|
}
|
|
// Installed using pkg file
|
|
path, err := downloadFileToTemporaryDir(ctx, urlWithVersionArch(pkgDownloadURL, targetVersion))
|
|
if err != nil {
|
|
return fmt.Errorf("error downloading update file: %w", err)
|
|
}
|
|
|
|
volume := "/"
|
|
for _, v := range strings.Split(string(outBytes), "\n") {
|
|
trimmed := strings.TrimSpace(v)
|
|
if strings.HasPrefix(trimmed, "volume: ") {
|
|
volume = strings.Split(trimmed, ": ")[1]
|
|
}
|
|
}
|
|
|
|
cmd = exec.CommandContext(ctx, "installer", "-pkg", path, "-target", volume)
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return fmt.Errorf("error running pkg file: %w", err)
|
|
}
|
|
err = cmd.Process.Release()
|
|
|
|
return err
|
|
}
|
|
|
|
func (u *UpdateManager) updateHomeBrew(ctx context.Context) error {
|
|
// Homebrew must be run as a non-root user
|
|
// To find out which user installed NetBird using HomeBrew we can check the owner of our brew tap directory
|
|
fileInfo, err := os.Stat("/opt/homebrew/Library/Taps/netbirdio/homebrew-tap/")
|
|
if err != nil {
|
|
return fmt.Errorf("error getting homebrew installation path info: %w", err)
|
|
}
|
|
|
|
fileSysInfo, ok := fileInfo.Sys().(*syscall.Stat_t)
|
|
if !ok {
|
|
return fmt.Errorf("error checking file owner, sysInfo type is %T not *syscall.Stat_t", fileInfo.Sys())
|
|
}
|
|
|
|
// Get username from UID
|
|
installer, err := user.LookupId(fmt.Sprintf("%d", fileSysInfo.Uid))
|
|
if err != nil {
|
|
return fmt.Errorf("error looking up brew installer user: %w", err)
|
|
}
|
|
userName := installer.Name
|
|
// Get user HOME, required for brew to run correctly
|
|
// https://github.com/Homebrew/brew/issues/15833
|
|
homeDir := installer.HomeDir
|
|
// Homebrew does not support installing specific versions
|
|
// Thus it will always update to latest and ignore targetVersion
|
|
upgradeArgs := []string{"-u", userName, "/opt/homebrew/bin/brew", "upgrade", "netbirdio/tap/netbird"}
|
|
// Check if netbird-ui is installed
|
|
cmd := exec.CommandContext(ctx, "brew", "info", "--json", "netbirdio/tap/netbird-ui")
|
|
err = cmd.Run()
|
|
if err == nil {
|
|
// netbird-ui is installed
|
|
upgradeArgs = append(upgradeArgs, "netbirdio/tap/netbird-ui")
|
|
}
|
|
cmd = exec.CommandContext(ctx, "sudo", upgradeArgs...)
|
|
cmd.Env = append(cmd.Env, "HOME="+homeDir)
|
|
|
|
// Homebrew upgrade doesn't restart the client on its own
|
|
// So we have to wait for it to finish running and ensure it's done
|
|
// And then basically restart the netbird service
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
return fmt.Errorf("error running brew upgrade: %w", err)
|
|
}
|
|
|
|
currentPID := os.Getpid()
|
|
|
|
// Restart netbird service after the fact
|
|
// This is a workaround since attempting to restart using launchctl will kill the service and die before starting
|
|
// the service again as it's a child process
|
|
// using SIGTERM should ensure a clean shutdown
|
|
process, err := os.FindProcess(currentPID)
|
|
if err != nil {
|
|
return fmt.Errorf("error finding current process: %w", err)
|
|
}
|
|
err = process.Signal(syscall.SIGTERM)
|
|
if err != nil {
|
|
return fmt.Errorf("error sending SIGTERM to current process: %w", err)
|
|
}
|
|
// We're dying now, which should restart us
|
|
|
|
return nil
|
|
}
|