Feature/update check (#1232)
Periodically fetch the latest available version, and the UI will shows a new menu for the download link. It checks both the daemon version and the UI version.
1
.github/workflows/release.yml
vendored
@ -17,6 +17,7 @@ on:
|
||||
- 'release_files/**'
|
||||
- '**/Dockerfile'
|
||||
- '**/Dockerfile.*'
|
||||
- 'client/ui/**'
|
||||
|
||||
env:
|
||||
SIGN_PIPE_VER: "v0.0.9"
|
||||
|
@ -54,7 +54,7 @@ nfpms:
|
||||
contents:
|
||||
- src: client/ui/netbird.desktop
|
||||
dst: /usr/share/applications/netbird.desktop
|
||||
- src: client/ui/disconnected.png
|
||||
- src: client/ui/netbird-systemtray-default.png
|
||||
dst: /usr/share/pixmaps/netbird.png
|
||||
dependencies:
|
||||
- netbird
|
||||
@ -71,7 +71,7 @@ nfpms:
|
||||
contents:
|
||||
- src: client/ui/netbird.desktop
|
||||
dst: /usr/share/applications/netbird.desktop
|
||||
- src: client/ui/disconnected.png
|
||||
- src: client/ui/netbird-systemtray-default.png
|
||||
dst: /usr/share/pixmaps/netbird.png
|
||||
dependencies:
|
||||
- netbird
|
||||
@ -91,4 +91,4 @@ uploads:
|
||||
mode: archive
|
||||
target: https://pkgs.wiretrustee.com/yum/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }}
|
||||
username: dev@wiretrustee.com
|
||||
method: PUT
|
||||
method: PUT
|
||||
|
@ -15,8 +15,10 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/app"
|
||||
@ -74,18 +76,30 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed connected.ico
|
||||
//go:embed netbird-systemtray-connected.ico
|
||||
var iconConnectedICO []byte
|
||||
|
||||
//go:embed connected.png
|
||||
//go:embed netbird-systemtray-connected.png
|
||||
var iconConnectedPNG []byte
|
||||
|
||||
//go:embed disconnected.ico
|
||||
//go:embed netbird-systemtray-default.ico
|
||||
var iconDisconnectedICO []byte
|
||||
|
||||
//go:embed disconnected.png
|
||||
//go:embed netbird-systemtray-default.png
|
||||
var iconDisconnectedPNG []byte
|
||||
|
||||
//go:embed netbird-systemtray-update.ico
|
||||
var iconUpdateICO []byte
|
||||
|
||||
//go:embed netbird-systemtray-update.png
|
||||
var iconUpdatePNG []byte
|
||||
|
||||
//go:embed netbird-systemtray-update-cloud.ico
|
||||
var iconUpdateCloudICO []byte
|
||||
|
||||
//go:embed netbird-systemtray-update-cloud.png
|
||||
var iconUpdateCloudPNG []byte
|
||||
|
||||
type serviceClient struct {
|
||||
ctx context.Context
|
||||
addr string
|
||||
@ -93,14 +107,20 @@ type serviceClient struct {
|
||||
|
||||
icConnected []byte
|
||||
icDisconnected []byte
|
||||
icUpdate []byte
|
||||
icUpdateCloud []byte
|
||||
|
||||
// systray menu items
|
||||
mStatus *systray.MenuItem
|
||||
mUp *systray.MenuItem
|
||||
mDown *systray.MenuItem
|
||||
mAdminPanel *systray.MenuItem
|
||||
mSettings *systray.MenuItem
|
||||
mQuit *systray.MenuItem
|
||||
mStatus *systray.MenuItem
|
||||
mUp *systray.MenuItem
|
||||
mDown *systray.MenuItem
|
||||
mAdminPanel *systray.MenuItem
|
||||
mSettings *systray.MenuItem
|
||||
mAbout *systray.MenuItem
|
||||
mVersionUI *systray.MenuItem
|
||||
mVersionDaemon *systray.MenuItem
|
||||
mUpdate *systray.MenuItem
|
||||
mQuit *systray.MenuItem
|
||||
|
||||
// application with main windows.
|
||||
app fyne.App
|
||||
@ -118,6 +138,11 @@ type serviceClient struct {
|
||||
managementURL string
|
||||
preSharedKey string
|
||||
adminURL string
|
||||
|
||||
update *version.Update
|
||||
daemonVersion string
|
||||
updateIndicationLock sync.Mutex
|
||||
isUpdateIconActive bool
|
||||
}
|
||||
|
||||
// newServiceClient instance constructor
|
||||
@ -130,14 +155,20 @@ func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient
|
||||
app: a,
|
||||
|
||||
showSettings: showSettings,
|
||||
update: version.NewUpdate(),
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
s.icConnected = iconConnectedICO
|
||||
s.icDisconnected = iconDisconnectedICO
|
||||
s.icUpdate = iconUpdateICO
|
||||
s.icUpdateCloud = iconUpdateCloudICO
|
||||
|
||||
} else {
|
||||
s.icConnected = iconConnectedPNG
|
||||
s.icDisconnected = iconDisconnectedPNG
|
||||
s.icUpdate = iconUpdatePNG
|
||||
s.icUpdateCloud = iconUpdateCloudPNG
|
||||
}
|
||||
|
||||
if showSettings {
|
||||
@ -328,19 +359,53 @@ func (s *serviceClient) updateStatus() error {
|
||||
return err
|
||||
}
|
||||
|
||||
s.updateIndicationLock.Lock()
|
||||
defer s.updateIndicationLock.Unlock()
|
||||
|
||||
var systrayIconState bool
|
||||
if status.Status == string(internal.StatusConnected) && !s.mUp.Disabled() {
|
||||
systray.SetIcon(s.icConnected)
|
||||
if !s.isUpdateIconActive {
|
||||
systray.SetIcon(s.icConnected)
|
||||
}
|
||||
systray.SetTooltip("NetBird (Connected)")
|
||||
s.mStatus.SetTitle("Connected")
|
||||
s.mUp.Disable()
|
||||
s.mDown.Enable()
|
||||
systrayIconState = true
|
||||
} else if status.Status != string(internal.StatusConnected) && s.mUp.Disabled() {
|
||||
systray.SetIcon(s.icDisconnected)
|
||||
if !s.isUpdateIconActive {
|
||||
systray.SetIcon(s.icDisconnected)
|
||||
}
|
||||
systray.SetTooltip("NetBird (Disconnected)")
|
||||
s.mStatus.SetTitle("Disconnected")
|
||||
s.mDown.Disable()
|
||||
s.mUp.Enable()
|
||||
systrayIconState = false
|
||||
}
|
||||
|
||||
// the updater struct notify by the upgrades available only, but if meanwhile the daemon has successfully
|
||||
// updated must reset the mUpdate visibility state
|
||||
if s.daemonVersion != status.DaemonVersion {
|
||||
s.mUpdate.Hide()
|
||||
s.daemonVersion = status.DaemonVersion
|
||||
|
||||
s.isUpdateIconActive = s.update.SetDaemonVersion(status.DaemonVersion)
|
||||
if !s.isUpdateIconActive {
|
||||
if systrayIconState {
|
||||
systray.SetIcon(s.icConnected)
|
||||
s.mAbout.SetIcon(s.icConnected)
|
||||
} else {
|
||||
systray.SetIcon(s.icDisconnected)
|
||||
s.mAbout.SetIcon(s.icDisconnected)
|
||||
}
|
||||
}
|
||||
|
||||
daemonVersionTitle := normalizedVersion(s.daemonVersion)
|
||||
s.mVersionDaemon.SetTitle(fmt.Sprintf("Daemon: %s", daemonVersionTitle))
|
||||
s.mVersionDaemon.SetTooltip(fmt.Sprintf("Daemon version: %s", daemonVersionTitle))
|
||||
s.mVersionDaemon.Show()
|
||||
}
|
||||
|
||||
return nil
|
||||
}, &backoff.ExponentialBackOff{
|
||||
InitialInterval: time.Second,
|
||||
@ -374,11 +439,24 @@ func (s *serviceClient) onTrayReady() {
|
||||
systray.AddSeparator()
|
||||
s.mSettings = systray.AddMenuItem("Settings", "Settings of the application")
|
||||
systray.AddSeparator()
|
||||
v := systray.AddMenuItem("v"+version.NetbirdVersion(), "Client Version: "+version.NetbirdVersion())
|
||||
v.Disable()
|
||||
|
||||
s.mAbout = systray.AddMenuItem("About", "About")
|
||||
s.mAbout.SetIcon(s.icDisconnected)
|
||||
versionString := normalizedVersion(version.NetbirdVersion())
|
||||
s.mVersionUI = s.mAbout.AddSubMenuItem(fmt.Sprintf("GUI: %s", versionString), fmt.Sprintf("GUI Version: %s", versionString))
|
||||
s.mVersionUI.Disable()
|
||||
|
||||
s.mVersionDaemon = s.mAbout.AddSubMenuItem("", "")
|
||||
s.mVersionDaemon.Disable()
|
||||
s.mVersionDaemon.Hide()
|
||||
|
||||
s.mUpdate = s.mAbout.AddSubMenuItem("Download latest version", "Download latest version")
|
||||
s.mUpdate.Hide()
|
||||
|
||||
systray.AddSeparator()
|
||||
s.mQuit = systray.AddMenuItem("Quit", "Quit the client app")
|
||||
|
||||
s.update.SetOnUpdateListener(s.onUpdateAvailable)
|
||||
go func() {
|
||||
s.getSrvConfig()
|
||||
for {
|
||||
@ -436,6 +514,11 @@ func (s *serviceClient) onTrayReady() {
|
||||
case <-s.mQuit.ClickedCh:
|
||||
systray.Quit()
|
||||
return
|
||||
case <-s.mUpdate.ClickedCh:
|
||||
err := openURL(version.DownloadUrl())
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("process connection: %v", err)
|
||||
@ -444,6 +527,14 @@ func (s *serviceClient) onTrayReady() {
|
||||
}()
|
||||
}
|
||||
|
||||
func normalizedVersion(version string) string {
|
||||
versionString := version
|
||||
if unicode.IsDigit(rune(versionString[0])) {
|
||||
versionString = fmt.Sprintf("v%s", versionString)
|
||||
}
|
||||
return versionString
|
||||
}
|
||||
|
||||
func (s *serviceClient) onTrayExit() {}
|
||||
|
||||
// getSrvClient connection to the service.
|
||||
@ -504,6 +595,32 @@ func (s *serviceClient) getSrvConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceClient) onUpdateAvailable() {
|
||||
s.updateIndicationLock.Lock()
|
||||
defer s.updateIndicationLock.Unlock()
|
||||
|
||||
s.mUpdate.Show()
|
||||
s.mAbout.SetIcon(s.icUpdateCloud)
|
||||
|
||||
s.isUpdateIconActive = true
|
||||
systray.SetIcon(s.icUpdate)
|
||||
}
|
||||
|
||||
func openURL(url string) error {
|
||||
var err error
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// checkPIDFile exists and return error, or write new.
|
||||
func checkPIDFile() error {
|
||||
pidFile := path.Join(os.TempDir(), "wiretrustee-ui.pid")
|
||||
|
Before Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 6.3 KiB |
BIN
client/ui/netbird-systemtray-connected.ico
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
client/ui/netbird-systemtray-connected.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
client/ui/netbird-systemtray-default.ico
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
client/ui/netbird-systemtray-default.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
client/ui/netbird-systemtray-update-cloud.ico
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
client/ui/netbird-systemtray-update-cloud.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
client/ui/netbird-systemtray-update.ico
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
client/ui/netbird-systemtray-update.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
@ -10,6 +10,7 @@ then
|
||||
wiretrustee service stop || true
|
||||
wiretrustee service uninstall || true
|
||||
fi
|
||||
|
||||
# check if netbird is installed
|
||||
NB_BIN=$(which netbird)
|
||||
if [ -z "$NB_BIN" ]
|
||||
@ -41,4 +42,4 @@ netbird service install 2> /dev/null || true
|
||||
netbird service start || true
|
||||
|
||||
# start app
|
||||
open /Applications/Netbird\ UI.app
|
||||
open /Applications/Netbird\ UI.app
|
||||
|
@ -8,6 +8,13 @@ AGENT=/usr/local/bin/netbird
|
||||
mkdir -p /var/log/netbird/
|
||||
|
||||
{
|
||||
# check if it was installed with brew
|
||||
brew list --formula | grep netbird
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "NetBird has been installed with Brew. Please use Brew to update the package."
|
||||
exit 1
|
||||
fi
|
||||
osascript -e 'quit app "Netbird"' || true
|
||||
$AGENT service stop || true
|
||||
|
||||
|
184
version/update.go
Normal file
@ -0,0 +1,184 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
goversion "github.com/hashicorp/go-version"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
fetchPeriod = 30 * time.Minute
|
||||
)
|
||||
|
||||
var (
|
||||
versionURL = "https://pkgs.netbird.io/releases/latest/version"
|
||||
)
|
||||
|
||||
// Update fetch the version info periodically and notify the onUpdateListener in case the UI version or the
|
||||
// daemon version are deprecated
|
||||
type Update struct {
|
||||
uiVersion *goversion.Version
|
||||
daemonVersion *goversion.Version
|
||||
latestAvailable *goversion.Version
|
||||
versionsLock sync.Mutex
|
||||
|
||||
fetchTicker *time.Ticker
|
||||
fetchDone chan struct{}
|
||||
|
||||
onUpdateListener func()
|
||||
listenerLock sync.Mutex
|
||||
}
|
||||
|
||||
// NewUpdate instantiate Update and start to fetch the new version information
|
||||
func NewUpdate() *Update {
|
||||
currentVersion, err := goversion.NewVersion(version)
|
||||
if err != nil {
|
||||
currentVersion, _ = goversion.NewVersion("0.0.0")
|
||||
}
|
||||
|
||||
latestAvailable, _ := goversion.NewVersion("0.0.0")
|
||||
|
||||
u := &Update{
|
||||
latestAvailable: latestAvailable,
|
||||
uiVersion: currentVersion,
|
||||
fetchTicker: time.NewTicker(fetchPeriod),
|
||||
fetchDone: make(chan struct{}),
|
||||
}
|
||||
go u.startFetcher()
|
||||
return u
|
||||
}
|
||||
|
||||
// StopWatch stop the version info fetch loop
|
||||
func (u *Update) StopWatch() {
|
||||
u.fetchTicker.Stop()
|
||||
|
||||
select {
|
||||
case u.fetchDone <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// SetDaemonVersion update the currently running daemon version. If new version is available it will trigger
|
||||
// the onUpdateListener
|
||||
func (u *Update) SetDaemonVersion(newVersion string) bool {
|
||||
daemonVersion, err := goversion.NewVersion(newVersion)
|
||||
if err != nil {
|
||||
daemonVersion, _ = goversion.NewVersion("0.0.0")
|
||||
}
|
||||
|
||||
u.versionsLock.Lock()
|
||||
if u.daemonVersion != nil && u.daemonVersion.Equal(daemonVersion) {
|
||||
u.versionsLock.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
u.daemonVersion = daemonVersion
|
||||
u.versionsLock.Unlock()
|
||||
return u.checkUpdate()
|
||||
}
|
||||
|
||||
// SetOnUpdateListener set new update listener
|
||||
func (u *Update) SetOnUpdateListener(updateFn func()) {
|
||||
u.listenerLock.Lock()
|
||||
defer u.listenerLock.Unlock()
|
||||
|
||||
u.onUpdateListener = updateFn
|
||||
if u.isUpdateAvailable() {
|
||||
u.onUpdateListener()
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Update) startFetcher() {
|
||||
changed := u.fetchVersion()
|
||||
if changed {
|
||||
u.checkUpdate()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-u.fetchDone:
|
||||
return
|
||||
case <-u.fetchTicker.C:
|
||||
changed := u.fetchVersion()
|
||||
if changed {
|
||||
u.checkUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Update) fetchVersion() bool {
|
||||
resp, err := http.Get(versionURL)
|
||||
if err != nil {
|
||||
log.Errorf("failed to fetch version info: %s", err)
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Errorf("invalid status code: %d", resp.StatusCode)
|
||||
return false
|
||||
}
|
||||
|
||||
if resp.ContentLength > 100 {
|
||||
log.Errorf("too large response: %d", resp.ContentLength)
|
||||
return false
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Errorf("failed to read content: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
latestAvailable, err := goversion.NewVersion(string(content))
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse the version string: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
u.versionsLock.Lock()
|
||||
defer u.versionsLock.Unlock()
|
||||
|
||||
if u.latestAvailable.Equal(latestAvailable) {
|
||||
return false
|
||||
}
|
||||
u.latestAvailable = latestAvailable
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (u *Update) checkUpdate() bool {
|
||||
if !u.isUpdateAvailable() {
|
||||
return false
|
||||
}
|
||||
|
||||
u.listenerLock.Lock()
|
||||
defer u.listenerLock.Unlock()
|
||||
if u.onUpdateListener == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
go u.onUpdateListener()
|
||||
return true
|
||||
}
|
||||
|
||||
func (u *Update) isUpdateAvailable() bool {
|
||||
u.versionsLock.Lock()
|
||||
defer u.versionsLock.Unlock()
|
||||
|
||||
if u.latestAvailable.GreaterThan(u.uiVersion) {
|
||||
return true
|
||||
}
|
||||
|
||||
if u.daemonVersion == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if u.latestAvailable.GreaterThan(u.daemonVersion) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
101
version/update_test.go
Normal file
@ -0,0 +1,101 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewUpdate(t *testing.T) {
|
||||
version = "1.0.0"
|
||||
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "10.0.0")
|
||||
}))
|
||||
defer svr.Close()
|
||||
versionURL = svr.URL
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
|
||||
onUpdate := false
|
||||
u := NewUpdate()
|
||||
defer u.StopWatch()
|
||||
u.SetOnUpdateListener(func() {
|
||||
onUpdate = true
|
||||
wg.Done()
|
||||
})
|
||||
|
||||
waitTimeout(wg)
|
||||
if onUpdate != true {
|
||||
t.Errorf("update not found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoNotUpdate(t *testing.T) {
|
||||
version = "11.0.0"
|
||||
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "10.0.0")
|
||||
}))
|
||||
defer svr.Close()
|
||||
versionURL = svr.URL
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
|
||||
onUpdate := false
|
||||
u := NewUpdate()
|
||||
defer u.StopWatch()
|
||||
u.SetOnUpdateListener(func() {
|
||||
onUpdate = true
|
||||
wg.Done()
|
||||
})
|
||||
|
||||
waitTimeout(wg)
|
||||
if onUpdate == true {
|
||||
t.Errorf("invalid update")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaemonUpdate(t *testing.T) {
|
||||
version = "11.0.0"
|
||||
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "11.0.0")
|
||||
}))
|
||||
defer svr.Close()
|
||||
versionURL = svr.URL
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
|
||||
onUpdate := false
|
||||
u := NewUpdate()
|
||||
defer u.StopWatch()
|
||||
u.SetOnUpdateListener(func() {
|
||||
onUpdate = true
|
||||
wg.Done()
|
||||
})
|
||||
|
||||
u.SetDaemonVersion("10.0.0")
|
||||
|
||||
waitTimeout(wg)
|
||||
if onUpdate != true {
|
||||
t.Errorf("invalid dameon version check")
|
||||
}
|
||||
}
|
||||
|
||||
func waitTimeout(wg *sync.WaitGroup) {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(c)
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
return
|
||||
}
|
||||
}
|
5
version/url.go
Normal file
@ -0,0 +1,5 @@
|
||||
package version
|
||||
|
||||
const (
|
||||
downloadURL = "https://app.netbird.io/install"
|
||||
)
|
33
version/url_darwin.go
Normal file
@ -0,0 +1,33 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
urlMacIntel = "https://pkgs.netbird.io/macos/amd64"
|
||||
urlMacM1M2 = "https://pkgs.netbird.io/macos/arm64"
|
||||
)
|
||||
|
||||
// DownloadUrl return with the proper download link
|
||||
func DownloadUrl() string {
|
||||
cmd := exec.Command("brew", "list --formula | grep -i netbird")
|
||||
if err := cmd.Start(); err != nil {
|
||||
goto PKGINSTALL
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err == nil {
|
||||
return downloadURL
|
||||
}
|
||||
|
||||
PKGINSTALL:
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
return urlMacIntel
|
||||
case "arm64":
|
||||
return urlMacM1M2
|
||||
default:
|
||||
return downloadURL
|
||||
}
|
||||
}
|
6
version/url_linux.go
Normal file
@ -0,0 +1,6 @@
|
||||
package version
|
||||
|
||||
// DownloadUrl return with the proper download link
|
||||
func DownloadUrl() string {
|
||||
return downloadURL
|
||||
}
|
19
version/url_windows.go
Normal file
@ -0,0 +1,19 @@
|
||||
package version
|
||||
|
||||
import "golang.org/x/sys/windows/registry"
|
||||
|
||||
const (
|
||||
urlWinExe = "https://pkgs.netbird.io/windows/x64"
|
||||
)
|
||||
|
||||
var regKeyAppPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Netbird"
|
||||
|
||||
// DownloadUrl return with the proper download link
|
||||
func DownloadUrl() string {
|
||||
_, err := registry.OpenKey(registry.LOCAL_MACHINE, regKeyAppPath, registry.QUERY_VALUE)
|
||||
if err == nil {
|
||||
return urlWinExe
|
||||
} else {
|
||||
return downloadURL
|
||||
}
|
||||
}
|