From e0737f94dceb7a2db9af704868b53d90adfc342a Mon Sep 17 00:00:00 2001 From: mlsmaycon Date: Sun, 12 Jul 2026 12:42:25 +0200 Subject: [PATCH] [release] Write fresh-install breadcrumb from installers Installers write a .fresh-install breadcrumb on fresh installs only and delete stale breadcrumbs on upgrade; none of them writes login items or registry Run keys. Windows NSIS detects upgrades via the uninstall registry entry or an existing installed executable; the macOS pkg via the previous pkgutil receipt; Linux deb/rpm via the standard postinstall arguments. Post-update GUI relaunches (macOS open, Linux ui-post-install.sh) pass --post-update so the first-run autostart default cannot fire on updates. --- client/ui/build/windows/nsis/project.nsi | 28 +++++++++++++-- release_files/darwin_pkg/postinstall | 30 ++++++++++++++++- release_files/ui-post-install.sh | 43 +++++++++++++++++++++--- 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/client/ui/build/windows/nsis/project.nsi b/client/ui/build/windows/nsis/project.nsi index 8d2530972..a9ef79afd 100644 --- a/client/ui/build/windows/nsis/project.nsi +++ b/client/ui/build/windows/nsis/project.nsi @@ -75,6 +75,8 @@ OutFile "..\..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the i InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). ShowInstDetails show # This will always show the installation details. +Var FreshInstall + Function .onInit !insertmacro wails.checkArchitecture FunctionEnd @@ -84,16 +86,38 @@ Section !insertmacro wails.webview2runtime + # Fresh-vs-upgrade detection must run before any file is written. An + # existing uninstall registry entry or an already-installed executable + # means upgrade; only a true fresh install gets the breadcrumb the GUI + # uses to enable launch-on-login by default. The installer itself never + # writes login items or Run keys. + StrCpy $FreshInstall "1" + ReadRegStr $0 HKLM "${UNINST_KEY}" "UninstallString" + StrCmp $0 "" +2 0 + StrCpy $FreshInstall "0" + IfFileExists "$INSTDIR\${PRODUCT_EXECUTABLE}" 0 +2 + StrCpy $FreshInstall "0" + SetOutPath $INSTDIR - + !insertmacro wails.files + # On upgrade, delete any stale breadcrumb (covers a fresh install where + # the GUI never launched before the first update). + StrCmp $FreshInstall "1" 0 removeBreadcrumb + FileOpen $0 "$INSTDIR\.fresh-install" w + FileClose $0 + Goto breadcrumbDone + removeBreadcrumb: + Delete "$INSTDIR\.fresh-install" + breadcrumbDone: + CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" !insertmacro wails.associateFiles !insertmacro wails.associateCustomProtocols - + !insertmacro wails.writeUninstaller SectionEnd diff --git a/release_files/darwin_pkg/postinstall b/release_files/darwin_pkg/postinstall index 33fa4bfee..cebbde64a 100755 --- a/release_files/darwin_pkg/postinstall +++ b/release_files/darwin_pkg/postinstall @@ -5,6 +5,8 @@ set -x APP=/Applications/NetBird.app AGENT=/usr/local/bin/netbird LOG_FILE=/var/log/netbird/client_post_install.log +FRESH_INSTALL_MARKER_DIR="/Library/Application Support/NetBird" +FRESH_INSTALL_MARKER="$FRESH_INSTALL_MARKER_DIR/.fresh-install" mkdir -p /var/log/netbird/ mkdir -p /usr/local/bin/ @@ -12,6 +14,25 @@ mkdir -p /usr/local/bin/ { echo "Installing NetBird..." + # Fresh-vs-upgrade detection: the pkg receipt of a previous install is + # still present while this script runs, so a receipt means upgrade. Only + # a true fresh install gets the breadcrumb the GUI uses to enable + # launch-on-login by default; the installer itself never writes login + # items. On upgrade any stale breadcrumb is removed (covers a fresh + # install where the GUI never launched before the first update). + IS_UPGRADE="false" + if [ -n "${INSTALL_PKG_SESSION_ID:-}" ] && pkgutil --pkg-info "${INSTALL_PKG_SESSION_ID}" > /dev/null 2>&1; then + IS_UPGRADE="true" + fi + if [ "$IS_UPGRADE" = "true" ]; then + echo "Upgrade detected (receipt for ${INSTALL_PKG_SESSION_ID} present), removing stale fresh-install marker." + rm -f "$FRESH_INSTALL_MARKER" + else + echo "Fresh install detected, writing fresh-install marker." + mkdir -p "$FRESH_INSTALL_MARKER_DIR" + touch "$FRESH_INSTALL_MARKER" + fi + if test -d $APP; then echo "NetBird app copied successfully." else @@ -30,7 +51,14 @@ mkdir -p /usr/local/bin/ $AGENT service install || true $AGENT service start || true - open $APP + # On upgrade the GUI relaunch carries --post-update so its first-run + # autostart default cannot fire (belt-and-suspenders on top of the + # absent breadcrumb). + if [ "$IS_UPGRADE" = "true" ]; then + open $APP --args --post-update + else + open $APP + fi echo "Finished Netbird installation successfully" exit 0 # all good diff --git a/release_files/ui-post-install.sh b/release_files/ui-post-install.sh index e2eb32cdc..127d2769f 100644 --- a/release_files/ui-post-install.sh +++ b/release_files/ui-post-install.sh @@ -3,8 +3,40 @@ set -e set -u -# Check if netbird-ui is running -pid="$(pgrep -x -f /usr/bin/netbird-ui || true)" +FRESH_INSTALL_MARKER_DIR="/var/lib/netbird" +FRESH_INSTALL_MARKER="$FRESH_INSTALL_MARKER_DIR/.fresh-install" + +# Fresh-vs-upgrade detection. rpm passes $1=1 on install and $1>=2 on +# upgrade; deb passes $1=configure with $2 empty on install and set to the +# previous version on upgrade. Anything unrecognized is treated as an +# upgrade so the breadcrumb is never written spuriously. +action="upgrade" +case "${1:-}" in + "1") + action="install" + ;; + "configure") + if [ -z "${2:-}" ]; then + action="install" + fi + ;; +esac + +# Only a true fresh install gets the breadcrumb the GUI uses to enable +# launch-on-login by default; the installer itself never writes autostart +# entries. On upgrade any stale breadcrumb is removed (covers a fresh +# install where the GUI never launched before the first update). +if [ "$action" = "install" ]; then + mkdir -p "$FRESH_INSTALL_MARKER_DIR" + touch "$FRESH_INSTALL_MARKER" +else + rm -f "$FRESH_INSTALL_MARKER" +fi + +# Check if netbird-ui is running. The pattern also matches an instance +# started with arguments (e.g. a previous --post-update relaunch), which the +# old exact-match -x pattern would miss. +pid="$(pgrep -f '^/usr/bin/netbird-ui( |$)' | head -n 1 || true)" if [ -n "${pid}" ] then uid="$(cat /proc/"${pid}"/loginuid)" @@ -13,7 +45,8 @@ then uid="$(stat -c '%u' /proc/"${pid}")" fi username="$(id -nu "${uid}")" - # Only re-run if it was already running - pkill -x -f /usr/bin/netbird-ui >/dev/null 2>&1 - su - "${username}" -c 'nohup /usr/bin/netbird-ui > /dev/null 2>&1 &' + # Only re-run if it was already running. The relaunch carries + # --post-update so the GUI's first-run autostart default cannot fire. + pkill -f '^/usr/bin/netbird-ui( |$)' >/dev/null 2>&1 || true + su - "${username}" -c 'nohup /usr/bin/netbird-ui --post-update > /dev/null 2>&1 &' fi