diff --git a/README.md b/README.md index a951928..4713ae5 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,13 @@ https://evilfox.win/ Некоторые AV (Bkav, Microsoft Wacapew/Wacatac, Ikarus Trojan.WinGo.Agent, Google, Trapmine) часто помечают **любые** неподписанные Go-бинарники с сетью и сменой системного прокси. +В 2.7.2: +- убран вечный цикл обновления (проверка больше не устанавливает сама; только кнопка «Обновить»); +- номер сборки в версии (`2.7.2+N` / FileVersion `2.7.2.N`); сравнение обновлений только по product semver. + +В 2.7.1: +- обновление только по кнопке «Обновить»; можно «Пропустить эту версию» (следующую всё равно предложит). + В 2.7.0: - GUI на macOS (тот же интерфейс, что на Windows): DMG → Navis.app открывает окно клиента; CLI лежит как `Navis-cli`. diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 2f0f5e1..8654969 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -12,8 +12,9 @@ android { applicationId = "win.evilfox.navis" minSdk = 26 targetSdk = 35 - versionCode = 270 - versionName = "2.7.0" + // versionCode = major*1_000_000 + minor*10_000 + patch*100 + build + versionCode = 2_070_201 + versionName = "2.7.2+1" vectorDrawables.useSupportLibrary = true ndk { abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64") diff --git a/build-macos.bat b/build-macos.bat index 112de32..f747ffe 100644 --- a/build-macos.bat +++ b/build-macos.bat @@ -1,21 +1,7 @@ @echo off -setlocal EnableDelayedExpansion +setlocal cd /d "%~dp0" -REM --- bump build number --- -for /f "usebackq delims=" %%i in (`python -c "from pathlib import Path; import re; t=Path('internal/update/version.go').read_text(); print(re.search(r'BaseVersion\\s*=\\s*\"([^\"]+)\"', t).group(1))"`) do set BASE_VERSION=%%i -if not exist buildnum.txt echo 0>buildnum.txt -set /p BUILD_NUM=buildnum.txt -set FULL_VERSION=%BASE_VERSION%.%BUILD_NUM% -echo Building macOS Navis %FULL_VERSION% ... - -python -c "from pathlib import Path; import re; p=Path('internal/update/version.go'); t=p.read_text(); p.write_text(re.sub(r'(var BuildNumber = \")[^\"]*(\")', r'\g<1>%BUILD_NUM%\2', t))" -python -c "import json; from pathlib import Path; maj,mi,pa=map(int,'%BASE_VERSION%'.split('.')[:3]); b=int('%BUILD_NUM%'); p=Path('versioninfo.json'); d=json.loads(p.read_text()); d['FixedFileInfo']['FileVersion']={'Major':maj,'Minor':mi,'Patch':pa,'Build':b}; d['FixedFileInfo']['ProductVersion']=d['FixedFileInfo']['FileVersion']; d['StringFileInfo']['FileVersion']=f'{maj}.{mi}.{pa}.{b}'; d['StringFileInfo']['ProductVersion']=d['StringFileInfo']['FileVersion']; p.write_text(json.dumps(d,indent=2)+chr(10))" - -set LDFLAGS=-s -w -X vpnclient/internal/update.BuildNumber=%BUILD_NUM% - mkdir "dist\navis-release\darwin-arm64" 2>nul mkdir "dist\navis-release\darwin-amd64" 2>nul mkdir "dist\navis-release\darwin-universal" 2>nul @@ -23,18 +9,16 @@ mkdir "dist\navis-release\darwin-universal" 2>nul set CGO_ENABLED=0 set GOOS=darwin set GOARCH=arm64 -go build -ldflags="%LDFLAGS%" -trimpath -o "dist\navis-release\darwin-arm64\Navis" .\cmd\vpnapp +go build -ldflags="-s -w" -o "dist\navis-release\darwin-arm64\Navis" .\cmd\vpnapp if errorlevel 1 exit /b 1 -go build -ldflags="%LDFLAGS%" -trimpath -o "dist\navis-release\darwin-arm64\Navis-cli" .\cmd\vpnclient +go build -ldflags="-s -w" -o "dist\navis-release\darwin-arm64\Navis-cli" .\cmd\vpnclient if errorlevel 1 exit /b 1 -echo %FULL_VERSION%> "dist\navis-release\darwin-arm64\VERSION" set GOARCH=amd64 -go build -ldflags="%LDFLAGS%" -trimpath -o "dist\navis-release\darwin-amd64\Navis" .\cmd\vpnapp +go build -ldflags="-s -w" -o "dist\navis-release\darwin-amd64\Navis" .\cmd\vpnapp if errorlevel 1 exit /b 1 -go build -ldflags="%LDFLAGS%" -trimpath -o "dist\navis-release\darwin-amd64\Navis-cli" .\cmd\vpnclient +go build -ldflags="-s -w" -o "dist\navis-release\darwin-amd64\Navis-cli" .\cmd\vpnclient if errorlevel 1 exit /b 1 -echo %FULL_VERSION%> "dist\navis-release\darwin-amd64\VERSION" set GOOS= set GOARCH= @@ -46,19 +30,18 @@ tools\mklipo\mklipo.exe "dist\navis-release\darwin-amd64\Navis" "dist\navis-rele if errorlevel 1 exit /b 1 tools\mklipo\mklipo.exe "dist\navis-release\darwin-amd64\Navis-cli" "dist\navis-release\darwin-arm64\Navis-cli" "dist\navis-release\darwin-universal\Navis-cli" if errorlevel 1 exit /b 1 -echo %FULL_VERSION%> "dist\navis-release\darwin-universal\VERSION" go build -o "tools\packmac\packmac.exe" .\tools\packmac if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-arm64\Navis" -out "dist\navis-release\darwin-arm64" -version %FULL_VERSION% -build %BUILD_NUM% -arch arm64 +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-arm64\Navis" -out "dist\navis-release\darwin-arm64" -version 2.7.2 -build 2.7.2.2 -arch arm64 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version %FULL_VERSION% -build %BUILD_NUM% -arch amd64 +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version 2.7.2 -build 2.7.2.2 -arch amd64 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-universal\Navis" -out "dist\navis-release\darwin-universal" -version %FULL_VERSION% -build %BUILD_NUM% -arch universal +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-universal\Navis" -out "dist\navis-release\darwin-universal" -version 2.7.2 -build 2.7.2.2 -arch universal if errorlevel 1 exit /b 1 -echo Built Mac GUI + CLI %FULL_VERSION%: +echo Built Mac GUI + CLI: dir "dist\navis-release\darwin-universal" dir "dist\navis-release\darwin-arm64" dir "dist\navis-release\darwin-amd64" diff --git a/build.bat b/build.bat index 761197b..9374f1e 100644 --- a/build.bat +++ b/build.bat @@ -1,19 +1,7 @@ @echo off -setlocal EnableDelayedExpansion +setlocal cd /d "%~dp0" -REM --- bump build number --- -for /f "usebackq delims=" %%i in (`python -c "from pathlib import Path; import re; t=Path('internal/update/version.go').read_text(); print(re.search(r'BaseVersion\\s*=\\s*\"([^\"]+)\"', t).group(1))"`) do set BASE_VERSION=%%i -if not exist buildnum.txt echo 0>buildnum.txt -set /p BUILD_NUM=buildnum.txt -set FULL_VERSION=%BASE_VERSION%.%BUILD_NUM% -echo Building Windows Navis %FULL_VERSION% ... - -python -c "from pathlib import Path; import re; p=Path('internal/update/version.go'); t=p.read_text(); p.write_text(re.sub(r'(var BuildNumber = \")[^\"]*(\")', r'\g<1>%BUILD_NUM%\2', t))" -python -c "import json; from pathlib import Path; maj,mi,pa=map(int,'%BASE_VERSION%'.split('.')[:3]); b=int('%BUILD_NUM%'); p=Path('versioninfo.json'); d=json.loads(p.read_text()); d['FixedFileInfo']['FileVersion']={'Major':maj,'Minor':mi,'Patch':pa,'Build':b}; d['FixedFileInfo']['ProductVersion']=d['FixedFileInfo']['FileVersion']; d['StringFileInfo']['FileVersion']=f'{maj}.{mi}.{pa}.{b}'; d['StringFileInfo']['ProductVersion']=d['StringFileInfo']['FileVersion']; p.write_text(json.dumps(d,indent=2)+chr(10))" - echo Generating Windows icon resources... go run ./tools/mkico if errorlevel 1 exit /b 1 @@ -26,21 +14,16 @@ if errorlevel 1 ( goversioninfo -64 -icon assets\navis.ico -manifest assets\app.manifest -o cmd\vpnapp\resource.syso versioninfo.json if errorlevel 1 exit /b 1 -set LDFLAGS=-H windowsgui -s -w -X vpnclient/internal/update.BuildNumber=%BUILD_NUM% - echo Building Navis GUI and CLI... -go build -ldflags="%LDFLAGS%" -trimpath -o Navis.exe ./cmd/vpnapp +go build -ldflags="-H windowsgui -s -w" -trimpath -o Navis.exe ./cmd/vpnapp if errorlevel 1 exit /b 1 -go build -ldflags="-s -w -X vpnclient/internal/update.BuildNumber=%BUILD_NUM%" -trimpath -o vpnclient.exe ./cmd/vpnclient +go build -ldflags="-s -w" -trimpath -o vpnclient.exe ./cmd/vpnclient if errorlevel 1 exit /b 1 -echo %FULL_VERSION%> Navis.version - echo. -echo Done: Navis %FULL_VERSION% +echo Done: echo Navis.exe echo vpnclient.exe -echo Navis.version echo assets\navis.ico endlocal diff --git a/buildnum.txt b/buildnum.txt deleted file mode 100644 index b8626c4..0000000 --- a/buildnum.txt +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/cmd/vpnapp/main_windows.go b/cmd/vpnapp/main_windows.go index ad3de2e..6c9a933 100644 --- a/cmd/vpnapp/main_windows.go +++ b/cmd/vpnapp/main_windows.go @@ -1,4 +1,4 @@ -//go:build windows +//go:build windows package main @@ -92,7 +92,7 @@ func main() { cfgPath: cfgPath, logBuf: logBuf, updateStatus: update.Status{ - Current: update.CurrentVersion, + Current: update.DisplayVersion(), ManifestURL: update.DefaultManifestURL, }, } @@ -204,7 +204,7 @@ func (a *app) getState() (uiState, error) { CorePath: corePath, ConfigPath: a.cfgPath, Profiles: cfg.ListProfiles(), - Version: update.CurrentVersion, + Version: update.DisplayVersion(), Update: a.updateStatus, Pings: append([]netcheck.Result(nil), a.pings...), Subscription: cfg.SubscriptionURL, @@ -418,7 +418,7 @@ func (a *app) checkUpdate() (update.Status, error) { st, err := update.Check(ctx, update.DefaultManifestURL) a.mu.Lock() if err != nil { - st.Current = update.CurrentVersion + st.Current = update.DisplayVersion() st.ManifestURL = update.DefaultManifestURL st.Error = err.Error() } @@ -428,6 +428,14 @@ func (a *app) checkUpdate() (update.Status, error) { } func (a *app) applyUpdate() (string, error) { + // Explicit user action only — never called from autoCheckUpdate. + st, err := a.checkUpdate() + if err != nil { + return "", err + } + if !st.Available { + return "", fmt.Errorf("обновление не требуется (текущая %s)", update.DisplayVersion()) + } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) defer cancel() latest, err := update.Apply(ctx, update.DefaultManifestURL) @@ -444,14 +452,11 @@ func (a *app) applyUpdate() (string, error) { } func (a *app) autoCheckUpdate() { + // Only refresh update status for the UI banner — never auto-install. + // (Previously auto-apply caused an infinite update loop when the feed version + // stayed ahead of the shipped CurrentVersion in the downloaded binary.) time.Sleep(3 * time.Second) - st, err := a.checkUpdate() - if err != nil || !st.Available { - return - } - // Auto-apply + relaunch when a newer build is published. - time.Sleep(1500 * time.Millisecond) - _, _ = a.applyUpdate() + _, _ = a.checkUpdate() } func openURL(raw string) error { diff --git a/dist/navis-release/Navis.exe b/dist/navis-release/Navis.exe index 5bd4c20..b72117e 100644 Binary files a/dist/navis-release/Navis.exe and b/dist/navis-release/Navis.exe differ diff --git a/dist/navis-release/android/Navis.apk b/dist/navis-release/android/Navis.apk index 4961a89..6678d44 100644 Binary files a/dist/navis-release/android/Navis.apk and b/dist/navis-release/android/Navis.apk differ diff --git a/dist/navis-release/darwin-amd64/Navis b/dist/navis-release/darwin-amd64/Navis index 418572f..e6f5ab6 100644 Binary files a/dist/navis-release/darwin-amd64/Navis and b/dist/navis-release/darwin-amd64/Navis differ diff --git a/dist/navis-release/darwin-amd64/Navis-cli b/dist/navis-release/darwin-amd64/Navis-cli index 814ba3a..d58ace4 100644 Binary files a/dist/navis-release/darwin-amd64/Navis-cli and b/dist/navis-release/darwin-amd64/Navis-cli differ diff --git a/dist/navis-release/darwin-amd64/Navis.app.zip b/dist/navis-release/darwin-amd64/Navis.app.zip index d221f2f..ea28e45 100644 Binary files a/dist/navis-release/darwin-amd64/Navis.app.zip and b/dist/navis-release/darwin-amd64/Navis.app.zip differ diff --git a/dist/navis-release/darwin-amd64/Navis.dmg b/dist/navis-release/darwin-amd64/Navis.dmg index ef9d960..18c6661 100644 Binary files a/dist/navis-release/darwin-amd64/Navis.dmg and b/dist/navis-release/darwin-amd64/Navis.dmg differ diff --git a/dist/navis-release/darwin-arm64/Navis b/dist/navis-release/darwin-arm64/Navis index b60126e..772d5e0 100755 Binary files a/dist/navis-release/darwin-arm64/Navis and b/dist/navis-release/darwin-arm64/Navis differ diff --git a/dist/navis-release/darwin-arm64/Navis-cli b/dist/navis-release/darwin-arm64/Navis-cli index 5ef1043..c910b5c 100755 Binary files a/dist/navis-release/darwin-arm64/Navis-cli and b/dist/navis-release/darwin-arm64/Navis-cli differ diff --git a/dist/navis-release/darwin-arm64/Navis.app.zip b/dist/navis-release/darwin-arm64/Navis.app.zip index bfe1132..7864ee8 100644 Binary files a/dist/navis-release/darwin-arm64/Navis.app.zip and b/dist/navis-release/darwin-arm64/Navis.app.zip differ diff --git a/dist/navis-release/darwin-arm64/Navis.dmg b/dist/navis-release/darwin-arm64/Navis.dmg index a75b0a6..f191dfa 100644 Binary files a/dist/navis-release/darwin-arm64/Navis.dmg and b/dist/navis-release/darwin-arm64/Navis.dmg differ diff --git a/dist/navis-release/darwin-arm64/Navis.version b/dist/navis-release/darwin-arm64/Navis.version index 388e070..344de1f 100644 --- a/dist/navis-release/darwin-arm64/Navis.version +++ b/dist/navis-release/darwin-arm64/Navis.version @@ -1 +1 @@ -2.7.2.4 +2.7.2+2 diff --git a/dist/navis-release/darwin-arm64/VERSION b/dist/navis-release/darwin-arm64/VERSION index 388e070..d45bd54 100644 --- a/dist/navis-release/darwin-arm64/VERSION +++ b/dist/navis-release/darwin-arm64/VERSION @@ -1 +1 @@ -2.7.2.4 +2.7.2.2 diff --git a/dist/navis-release/darwin-universal/Navis b/dist/navis-release/darwin-universal/Navis index 0191cda..aa31ff9 100644 Binary files a/dist/navis-release/darwin-universal/Navis and b/dist/navis-release/darwin-universal/Navis differ diff --git a/dist/navis-release/darwin-universal/Navis-cli b/dist/navis-release/darwin-universal/Navis-cli index 4a6be2b..ea041e8 100644 Binary files a/dist/navis-release/darwin-universal/Navis-cli and b/dist/navis-release/darwin-universal/Navis-cli differ diff --git a/dist/navis-release/darwin-universal/Navis.app.zip b/dist/navis-release/darwin-universal/Navis.app.zip index d16bbac..7466656 100644 Binary files a/dist/navis-release/darwin-universal/Navis.app.zip and b/dist/navis-release/darwin-universal/Navis.app.zip differ diff --git a/dist/navis-release/darwin-universal/Navis.dmg b/dist/navis-release/darwin-universal/Navis.dmg index eb2e2f5..4f7d492 100644 Binary files a/dist/navis-release/darwin-universal/Navis.dmg and b/dist/navis-release/darwin-universal/Navis.dmg differ diff --git a/dist/navis-release/update.json b/dist/navis-release/update.json index 63ce6f3..5451bf0 100644 --- a/dist/navis-release/update.json +++ b/dist/navis-release/update.json @@ -1,22 +1,22 @@ { - "version": "2.7.2.4", - "notes": "Navis 2.7.2.4", + "version": "2.7.2", + "notes": "Navis 2.7.2+2: иконка приложения (N), macOS AppIcon; обновление только по кнопке.", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "60392f389c9ed48b11124ce440d787ab7b051e7593833f15172e1c09434245dd", + "sha256": "9a64a3b059c53eb818ff696d5f446c39f52e243508f92d05365dd06a05c57a1e", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "60392f389c9ed48b11124ce440d787ab7b051e7593833f15172e1c09434245dd", + "sha256": "9a64a3b059c53eb818ff696d5f446c39f52e243508f92d05365dd06a05c57a1e", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "4f1f34f57ed7cd588f9d62f24d074041be3f3b1504f18e8203c953536236c381", + "sha256": "0a6d7297e132f9835e2b82f5f28ec1d4e2a51fc070343f8c701e019ac57a2e86", "os": "darwin", "arch": "arm64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", @@ -24,7 +24,7 @@ }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "e71d7c0080e9acdcaa2355e4dd8fa3ccec8d4ed24646e27dbc1a814b0a4019cc", + "sha256": "8072cacd79fa95da3f3230b88b8e09e380ed2e9f634703f579950952b94f774d", "os": "darwin", "arch": "amd64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", @@ -32,7 +32,7 @@ }, "darwin-universal": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis", - "sha256": "6b58907bb81583cf1730b34012cce97e7f758cf3eb12e630c971d0232cce5262", + "sha256": "f50642373f615fa55117d1bbe7b2f86f2a19ab03a9ae53cc7e7d137b432ebbe5", "os": "darwin", "arch": "universal", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis.dmg", diff --git a/dist/navis-release/vpnclient.exe b/dist/navis-release/vpnclient.exe new file mode 100644 index 0000000..00eb4d6 Binary files /dev/null and b/dist/navis-release/vpnclient.exe differ diff --git a/dist/update.json b/dist/update.json index 63ce6f3..5451bf0 100644 --- a/dist/update.json +++ b/dist/update.json @@ -1,22 +1,22 @@ { - "version": "2.7.2.4", - "notes": "Navis 2.7.2.4", + "version": "2.7.2", + "notes": "Navis 2.7.2+2: иконка приложения (N), macOS AppIcon; обновление только по кнопке.", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "60392f389c9ed48b11124ce440d787ab7b051e7593833f15172e1c09434245dd", + "sha256": "9a64a3b059c53eb818ff696d5f446c39f52e243508f92d05365dd06a05c57a1e", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "60392f389c9ed48b11124ce440d787ab7b051e7593833f15172e1c09434245dd", + "sha256": "9a64a3b059c53eb818ff696d5f446c39f52e243508f92d05365dd06a05c57a1e", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "4f1f34f57ed7cd588f9d62f24d074041be3f3b1504f18e8203c953536236c381", + "sha256": "0a6d7297e132f9835e2b82f5f28ec1d4e2a51fc070343f8c701e019ac57a2e86", "os": "darwin", "arch": "arm64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", @@ -24,7 +24,7 @@ }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "e71d7c0080e9acdcaa2355e4dd8fa3ccec8d4ed24646e27dbc1a814b0a4019cc", + "sha256": "8072cacd79fa95da3f3230b88b8e09e380ed2e9f634703f579950952b94f774d", "os": "darwin", "arch": "amd64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", @@ -32,7 +32,7 @@ }, "darwin-universal": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis", - "sha256": "6b58907bb81583cf1730b34012cce97e7f758cf3eb12e630c971d0232cce5262", + "sha256": "f50642373f615fa55117d1bbe7b2f86f2a19ab03a9ae53cc7e7d137b432ebbe5", "os": "darwin", "arch": "universal", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis.dmg", diff --git a/internal/apphost/app.go b/internal/apphost/app.go index 94be36e..f33dcb9 100644 --- a/internal/apphost/app.go +++ b/internal/apphost/app.go @@ -70,7 +70,7 @@ func New(mgr *core.Manager, cfgPath string, logBuf *bytes.Buffer) *App { CfgPath: cfgPath, LogBuf: logBuf, UpdateStatus: update.Status{ - Current: update.CurrentVersion, + Current: update.DisplayVersion(), ManifestURL: update.DefaultManifestURL, }, } @@ -125,7 +125,7 @@ func (a *App) GetState() (UIState, error) { CorePath: corePath, ConfigPath: a.CfgPath, Profiles: cfg.ListProfiles(), - Version: update.CurrentVersion, + Version: update.DisplayVersion(), Update: a.UpdateStatus, Pings: append([]netcheck.Result(nil), a.Pings...), Subscription: cfg.SubscriptionURL, @@ -322,7 +322,7 @@ func (a *App) CheckUpdate() (update.Status, error) { st, err := update.Check(ctx, update.DefaultManifestURL) a.mu.Lock() if err != nil { - st.Current = update.CurrentVersion + st.Current = update.DisplayVersion() st.ManifestURL = update.DefaultManifestURL st.Error = err.Error() } @@ -332,6 +332,14 @@ func (a *App) CheckUpdate() (update.Status, error) { } func (a *App) ApplyUpdate() (string, error) { + // Explicit user action only — never called from AutoCheckUpdate. + st, err := a.CheckUpdate() + if err != nil { + return "", err + } + if !st.Available { + return "", fmt.Errorf("обновление не требуется (текущая %s)", update.DisplayVersion()) + } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) defer cancel() latest, err := update.Apply(ctx, update.DefaultManifestURL) @@ -351,13 +359,10 @@ func (a *App) ApplyUpdate() (string, error) { } func (a *App) AutoCheckUpdate() { + // Only check and populate status for the banner — never auto-apply. + // Auto-apply previously caused endless download→relaunch loops on Mac/Windows. time.Sleep(3 * time.Second) - st, err := a.CheckUpdate() - if err != nil || !st.Available { - return - } - time.Sleep(1500 * time.Millisecond) - _, _ = a.ApplyUpdate() + _, _ = a.CheckUpdate() } func (a *App) OpenShopURL(raw string) error { diff --git a/internal/appui/index.html b/internal/appui/index.html index d365627..5e51d29 100644 --- a/internal/appui/index.html +++ b/internal/appui/index.html @@ -76,6 +76,15 @@ color: var(--muted); font-size: .84rem; } + .update-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + .update-actions .action { + flex: 1 1 auto; + min-width: 140px; + } .top { display: flex; @@ -712,7 +721,10 @@
Доступно обновление

- +
+ + +
@@ -941,7 +953,18 @@ const updCheckBtn = $("updCheckBtn"); const updateBanner = $("updateBanner"); const updateBtn = $("updateBtn"); + const skipUpdateBtn = $("skipUpdateBtn"); const verLabel = $("verLabel"); + const SKIP_UPDATE_KEY = "navis.skipUpdateVersion"; + function skippedVersion() { + try { return localStorage.getItem(SKIP_UPDATE_KEY) || ""; } catch (_) { return ""; } + } + function setSkippedVersion(v) { + try { + if (v) localStorage.setItem(SKIP_UPDATE_KEY, String(v)); + else localStorage.removeItem(SKIP_UPDATE_KEY); + } catch (_) {} + } const quitBtn = $("quitBtn"); if (window.__navisHttp && quitBtn) { quitBtn.hidden = false; @@ -1178,10 +1201,17 @@ updateBanner.classList.remove("show"); return; } + const latest = (u.latest || "").toString(); + const skip = skippedVersion(); + if (u.available && latest && skip === latest) { + updateBanner.classList.remove("show"); + return; + } if (u.available) { updateBanner.classList.add("show"); - $("updateTitle").textContent = "Обновление " + u.latest; - $("updateNotes").textContent = u.notes || ("Текущая версия " + u.current); + $("updateTitle").textContent = "Доступно обновление " + latest; + $("updateNotes").textContent = u.notes || ("У вас " + (u.current || version || "?") + ". Обновление только по кнопке."); + updateBanner.dataset.latest = latest; } else { updateBanner.classList.remove("show"); } @@ -1284,7 +1314,7 @@ } function paintButtonsLocked(locked) { - [btn, coreBtn, saveBtn, addBtn, delBtn, profile, pingBtn, bestBtn, updCheckBtn, updateBtn, subBtn, subUrl].forEach((b) => { + [btn, coreBtn, saveBtn, addBtn, delBtn, profile, pingBtn, bestBtn, updCheckBtn, updateBtn, skipUpdateBtn, subBtn, subUrl].forEach((b) => { if (b) b.disabled = locked; }); } @@ -1449,6 +1479,13 @@ } catch (e) { setMeta(String(e), "err"); } })); + skipUpdateBtn.addEventListener("click", () => { + const latest = updateBanner.dataset.latest || ""; + if (latest) setSkippedVersion(latest); + updateBanner.classList.remove("show"); + setMeta("Версия " + (latest || "?") + " пропущена. Следующую предложим.", "ok"); + }); + (async () => { try { await refresh({ syncForm: true }); diff --git a/internal/update/update.go b/internal/update/update.go index 63c5936..483e7e3 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -16,9 +16,27 @@ import ( "time" ) +// CurrentVersion is the product/semver used for update eligibility (feed "version"). +// Keep major.minor.patch only — no build suffix here. +const CurrentVersion = "2.7.2" + +// BuildNumber is the monotonic build within CurrentVersion (Windows FileVersion 4th part, +// macOS CFBundleVersion suffix, Android versionCode low digits). Bump on every release build. +const BuildNumber = 2 + // DefaultManifestURL is the update feed (hosted in the project git repo). const DefaultManifestURL = "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/update.json" +// DisplayVersion is shown in the UI (product + build), e.g. "2.7.2+1". +func DisplayVersion() string { + return fmt.Sprintf("%s+%d", CurrentVersion, BuildNumber) +} + +// FileVersionDots is Major.Minor.Patch.Build for Windows resources / CFBundleVersion. +func FileVersionDots() string { + return fmt.Sprintf("%s.%d", CurrentVersion, BuildNumber) +} + // PlatformAsset is one OS/arch binary in the feed. type PlatformAsset struct { URL string `json:"url"` @@ -87,7 +105,7 @@ func Check(ctx context.Context, manifestURL string) (Status, error) { manifestURL = DefaultManifestURL } key := PlatformKey() - st := Status{Current: CurrentVersion, ManifestURL: manifestURL, Platform: key} + st := Status{Current: DisplayVersion(), ManifestURL: manifestURL, Platform: key} m, err := fetchManifest(ctx, manifestURL) if err != nil { @@ -106,38 +124,46 @@ func Check(ctx context.Context, manifestURL string) (Status, error) { } st.URL = url st.SHA256 = sha + // Product-only compare: build numbers must not keep offering an update after install + // when feed version equals CurrentVersion (e.g. 2.7.2 vs local 2.7.2+1). st.Available = Compare(st.Latest, CurrentVersion) > 0 && url != "" return st, nil } -// Compare returns 1 if a>b, -1 if ab, -1 if a n { - n = len(bs) - } - for i := 0; i < n; i++ { - var ai, bi int - if i < len(as) { - ai = as[i] - } - if i < len(bs) { - bi = bs[i] - } - if ai > bi { + as := productParts(a) + bs := productParts(b) + for i := 0; i < 3; i++ { + if as[i] > bs[i] { return 1 } - if ai < bi { + if as[i] < bs[i] { return -1 } } return 0 } +func productParts(v string) [3]int { + parts := splitVer(v) + var out [3]int + for i := 0; i < 3 && i < len(parts); i++ { + out[i] = parts[i] + } + return out +} + func splitVer(v string) []int { v = strings.TrimPrefix(strings.TrimSpace(v), "v") + // Strip build metadata: 2.7.2+1 / 2.7.2-beta → compare product only. + if i := strings.IndexAny(v, "+-"); i >= 0 { + v = v[:i] + } parts := strings.Split(v, ".") out := make([]int, 0, len(parts)) for _, p := range parts { @@ -146,9 +172,6 @@ func splitVer(v string) []int { out = append(out, 0) continue } - if i := strings.IndexAny(p, "-+"); i >= 0 { - p = p[:i] - } n, _ := strconv.Atoi(p) out = append(out, n) } @@ -169,7 +192,7 @@ func fetchManifest(ctx context.Context, manifestURL string) (Manifest, error) { if err != nil { return Manifest{}, err } - req.Header.Set("User-Agent", "Navis/"+CurrentVersion) + req.Header.Set("User-Agent", "Navis/"+DisplayVersion()) req.Header.Set("Accept", "application/json") client := &http.Client{Timeout: 20 * time.Second} resp, err := client.Do(req) @@ -192,7 +215,7 @@ func downloadFile(ctx context.Context, url, dest string) error { if err != nil { return err } - req.Header.Set("User-Agent", "Navis/"+CurrentVersion) + req.Header.Set("User-Agent", "Navis/"+DisplayVersion()) client := &http.Client{Timeout: 10 * time.Minute} resp, err := client.Do(req) if err != nil { @@ -224,7 +247,8 @@ func fileSHA256(path string) (string, error) { return hex.EncodeToString(h.Sum(nil)), nil } -// prepareDownload fetches the update next to the running binary as ".new". +// prepareDownload fetches the update next to the running binary as temp download. +// Refuses when already on/newer than the feed product version (no re-apply loop). func prepareDownload(ctx context.Context, manifestURL string) (latest, url, sha, exePath, tmpPath string, err error) { st, err := Check(ctx, manifestURL) if err != nil { @@ -234,7 +258,7 @@ func prepareDownload(ctx context.Context, manifestURL string) (latest, url, sha, if st.Error != "" { return "", "", "", "", "", fmt.Errorf("%s", st.Error) } - return "", "", "", "", "", fmt.Errorf("обновление не найдено") + return "", "", "", "", "", fmt.Errorf("уже актуальная версия %s (канал %s)", DisplayVersion(), st.Latest) } if !allowedDownloadURL(st.URL) { return "", "", "", "", "", fmt.Errorf("URL обновления должен быть на git.evilfox.cc или files.de4ima.uk") @@ -247,6 +271,9 @@ func prepareDownload(ctx context.Context, manifestURL string) (latest, url, sha, if err != nil { return "", "", "", "", "", err } + if resolved, err := filepath.EvalSymlinks(exe); err == nil && resolved != "" { + exe = resolved + } tmp := filepath.Join(filepath.Dir(exe), ".navis-update-download") _ = os.Remove(tmp) // Also clear legacy leftover from older updaters. diff --git a/internal/update/update_test.go b/internal/update/update_test.go index 2b3a149..66664d2 100644 --- a/internal/update/update_test.go +++ b/internal/update/update_test.go @@ -15,6 +15,19 @@ func TestCompare(t *testing.T) { if Compare("1.1.0", "1.1.0") != 0 { t.Fatal("expected equal") } + // Build must not make same product look outdated or newer for update checks. + if Compare("2.7.2", "2.7.2+9") != 0 { + t.Fatal("expected equal ignoring +build") + } + if Compare("2.7.2", "2.7.2.99") != 0 { + t.Fatal("expected equal ignoring 4th component") + } + if Compare("2.7.3", "2.7.2+1") <= 0 { + t.Fatal("expected product bump to win") + } + if DisplayVersion() == "" || !strings.Contains(DisplayVersion(), "+") { + t.Fatal("DisplayVersion should include build") + } } func TestManifestAssetFor(t *testing.T) { diff --git a/internal/update/version.go b/internal/update/version.go deleted file mode 100644 index 3753d50..0000000 --- a/internal/update/version.go +++ /dev/null @@ -1,31 +0,0 @@ -package update - -import "strings" - -// BaseVersion is the marketing / release version (MAJOR.MINOR.PATCH). -const BaseVersion = "2.7.2" - -// BuildNumber is the incremental build counter. -// Override at link time: -// -// -ldflags "-X vpnclient/internal/update.BuildNumber=42" -// -// Source default is bumped by build scripts via buildnum.txt. -var BuildNumber = "4" - -// CurrentVersion is BaseVersion.BuildNumber, e.g. "2.7.2.1". -// Set in init so -ldflags BuildNumber is applied first. -var CurrentVersion string - -func init() { - CurrentVersion = FullVersion() -} - -// FullVersion returns "MAJOR.MINOR.PATCH.BUILD". -func FullVersion() string { - b := strings.TrimSpace(BuildNumber) - if b == "" { - b = "0" - } - return BaseVersion + "." + b -} diff --git a/scripts/build-macos-arm64.sh b/scripts/build-macos-arm64.sh index b305b9f..b23aa9e 100755 --- a/scripts/build-macos-arm64.sh +++ b/scripts/build-macos-arm64.sh @@ -1,67 +1,39 @@ #!/bin/bash -# Build Navis GUI for Apple Silicon (arm64) with auto-incremented build number. +# Build Navis GUI for Apple Silicon (arm64). set -euo pipefail cd "$(dirname "$0")/.." -BASE_VERSION="$(python3 - <<'PY' +VERSION="$(python3 - <<'PY' import re from pathlib import Path -t = Path('internal/update/version.go').read_text() -m = re.search(r'BaseVersion\s*=\s*"([^"]+)"', t) +t = Path('internal/update/update.go').read_text() +m = re.search(r'CurrentVersion\s*=\s*"([^"]+)"', t) print(m.group(1) if m else '0.0.0') PY )" - -BUILD_NUM="$(tr -d '[:space:]' < buildnum.txt 2>/dev/null || echo 0)" -if ! [[ "$BUILD_NUM" =~ ^[0-9]+$ ]]; then BUILD_NUM=0; fi -BUILD_NUM=$((BUILD_NUM + 1)) -echo "$BUILD_NUM" > buildnum.txt - -FULL_VERSION="${BASE_VERSION}.${BUILD_NUM}" -echo "Building Navis ${FULL_VERSION} (arm64)..." - -# Keep source BuildNumber in sync for non-ldflags runs / IDE -python3 - <${BUILD_NUM}\2', t) -p.write_text(t2) -PY - -# versioninfo.json (Windows metadata; also documents build) -python3 - <', d['StringFileInfo']['FileVersion']) +t = Path('internal/update/update.go').read_text() +m = re.search(r'const BuildNumber\s*=\s*(\d+)', t) +print(m.group(1) if m else '0') PY +)" +FULL="${VERSION}.${BUILD}" +echo "Building Navis ${VERSION}+${BUILD} (arm64)..." OUT="dist/navis-release/darwin-arm64" mkdir -p "$OUT" -LDFLAGS="-s -w -X vpnclient/internal/update.BuildNumber=${BUILD_NUM}" +LDFLAGS="-s -w" +export PATH="/tmp/go-sdk/go/bin:/usr/local/go/bin:$PATH" CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 \ go build -ldflags="$LDFLAGS" -trimpath -o "$OUT/Navis" ./cmd/vpnapp CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 \ go build -ldflags="$LDFLAGS" -trimpath -o "$OUT/Navis-cli" ./cmd/vpnclient -# Stamp plain-text version next to binaries -printf '%s\n' "$FULL_VERSION" > "$OUT/VERSION" -printf '%s\n' "$FULL_VERSION" > "$OUT/Navis.version" +printf '%s\n' "$FULL" > "$OUT/VERSION" +printf '%s\n' "${VERSION}+${BUILD}" > "$OUT/Navis.version" if command -v codesign >/dev/null 2>&1; then codesign -s - --force "$OUT/Navis" @@ -72,35 +44,35 @@ go build -o tools/packmac/packmac ./tools/packmac tools/packmac/packmac \ -bin "$OUT/Navis" \ -out "$OUT" \ - -version "$FULL_VERSION" \ - -build "$BUILD_NUM" \ + -version "$VERSION" \ + -build "$FULL" \ -arch arm64 -# Rename release copies with version in filename (keep unversioned too) -cp -f "$OUT/Navis.dmg" "$OUT/Navis-${FULL_VERSION}-arm64.dmg" -cp -f "$OUT/Navis.app.zip" "$OUT/Navis-${FULL_VERSION}-arm64.app.zip" +cp -f "$OUT/Navis.dmg" "$OUT/Navis-${FULL}-arm64.dmg" +cp -f "$OUT/Navis.app.zip" "$OUT/Navis-${FULL}-arm64.app.zip" -# update.json python3 - <', full) + print('updated', p, '->', ver, 'sha', h[:12]) PY echo "" -echo "Done: Navis ${FULL_VERSION}" +echo "Done: Navis ${VERSION}+${BUILD}" ls -lh "$OUT" -file "$OUT/Navis.dmg" diff --git a/scripts/install-macos.sh b/scripts/install-macos.sh index 81bd468..8b27b64 100644 --- a/scripts/install-macos.sh +++ b/scripts/install-macos.sh @@ -17,7 +17,7 @@ DEST="${1:-$HOME/Downloads/Navis.dmg}" UNIVERSAL="$BASE/darwin-universal/Navis.dmg" SPECIFIC="$BASE/$SUBDIR/Navis.dmg" -echo "Mac: $ARCH · ставим Navis GUI 2.7" +echo "Mac: $ARCH · ставим Navis GUI 2.7.1" echo "Скачиваю универсальный DMG…" if ! curl -fL -o "$DEST" "$UNIVERSAL"; then echo "Universal недоступен, скачиваю $SUBDIR…" diff --git a/server/update.json b/server/update.json index fe04ef1..5dd969b 100644 --- a/server/update.json +++ b/server/update.json @@ -1,22 +1,22 @@ { - "version": "2.7.0", - "notes": "GUI на macOS (как на Windows). Откройте Navis.app — окно клиента. CLI: Navis-cli", + "version": "2.7.2", + "notes": "2.7.2: исправлен вечный цикл обновления (только кнопка «Обновить», без автоустановки); номер сборки 2.7.2+N; можно «Пропустить эту версию».", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "60392f389c9ed48b11124ce440d787ab7b051e7593833f15172e1c09434245dd", + "sha256": "9a64a3b059c53eb818ff696d5f446c39f52e243508f92d05365dd06a05c57a1e", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "60392f389c9ed48b11124ce440d787ab7b051e7593833f15172e1c09434245dd", + "sha256": "9a64a3b059c53eb818ff696d5f446c39f52e243508f92d05365dd06a05c57a1e", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "dd15b856bed836b82f2892e64db4db71771a947f3a392ce7d9f9e7f1b59edf1d", + "sha256": "8fa7fcb40d4165d20d3f81840c84f729af034b54dd4698ea144c7145c85872eb", "os": "darwin", "arch": "arm64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", @@ -24,7 +24,7 @@ }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "e71d7c0080e9acdcaa2355e4dd8fa3ccec8d4ed24646e27dbc1a814b0a4019cc", + "sha256": "8072cacd79fa95da3f3230b88b8e09e380ed2e9f634703f579950952b94f774d", "os": "darwin", "arch": "amd64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", @@ -32,11 +32,11 @@ }, "darwin-universal": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis", - "sha256": "6b58907bb81583cf1730b34012cce97e7f758cf3eb12e630c971d0232cce5262", + "sha256": "f50642373f615fa55117d1bbe7b2f86f2a19ab03a9ae53cc7e7d137b432ebbe5", "os": "darwin", "arch": "universal", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis.dmg", "zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis.app.zip" } } -} +} \ No newline at end of file diff --git a/tools/packmac/main.go b/tools/packmac/main.go index 909d9bb..f238f72 100644 --- a/tools/packmac/main.go +++ b/tools/packmac/main.go @@ -19,33 +19,25 @@ import ( func main() { bin := flag.String("bin", "", "path to darwin Navis binary") outDir := flag.String("out", "", "output directory (e.g. dist/navis-release/darwin-arm64)") - version := flag.String("version", "1.4.1", "CFBundleShortVersionString (e.g. 2.7.2.3)") - build := flag.String("build", "", "CFBundleVersion build number (default: last segment of -version)") + version := flag.String("version", "1.4.1", "CFBundleShortVersionString") + build := flag.String("build", "", "CFBundleVersion (defaults to -version)") arch := flag.String("arch", "arm64", "arch label for volume name") flag.Parse() if *bin == "" || *outDir == "" { - fmt.Fprintln(os.Stderr, "usage: packmac -bin -out [-version 2.7.2.3] [-build 3] [-arch arm64]") + fmt.Fprintln(os.Stderr, "usage: packmac -bin -out [-version 2.7.2] [-build 2.7.2.1] [-arch arm64]") os.Exit(2) } - buildNo := *build - if buildNo == "" { - buildNo = lastVersionSegment(*version) + bundleVer := *build + if bundleVer == "" { + bundleVer = *version } - if err := run(*bin, *outDir, *version, buildNo, *arch); err != nil { + if err := run(*bin, *outDir, *version, bundleVer, *arch); err != nil { fmt.Fprintf(os.Stderr, "packmac: %v\n", err) os.Exit(1) } } -func lastVersionSegment(v string) string { - v = strings.TrimSpace(v) - if i := strings.LastIndex(v, "."); i >= 0 && i+1 < len(v) { - return v[i+1:] - } - return v -} - -func run(binPath, outDir, version, buildNo, arch string) error { +func run(binPath, outDir, version, bundleVersion, arch string) error { if err := os.MkdirAll(outDir, 0o755); err != nil { return err } @@ -56,7 +48,7 @@ func run(binPath, outDir, version, buildNo, arch string) error { defer os.RemoveAll(stage) appRoot := filepath.Join(stage, "Navis.app") - if err := writeAppBundle(appRoot, binPath, version, buildNo, arch); err != nil { + if err := writeAppBundle(appRoot, binPath, version, bundleVersion, arch); err != nil { return err } if err := signAppBundle(appRoot); err != nil { @@ -136,7 +128,7 @@ func writeHdiutilDMG(dmgPath, stageDir, volume string) error { return nil } -func writeAppBundle(appRoot, binPath, version, buildNo, arch string) error { +func writeAppBundle(appRoot, binPath, version, bundleVersion, arch string) error { macOSDir := filepath.Join(appRoot, "Contents", "MacOS") resDir := filepath.Join(appRoot, "Contents", "Resources") if err := os.MkdirAll(macOSDir, 0o755); err != nil { @@ -163,7 +155,7 @@ func writeAppBundle(appRoot, binPath, version, buildNo, arch string) error { CFBundleInfoDictionaryVersion6.0%s -`, version, buildNo, archPriority) +`, version, bundleVersion, archPriority) if err := os.WriteFile(filepath.Join(appRoot, "Contents", "Info.plist"), []byte(plist), 0o644); err != nil { return err } @@ -179,39 +171,9 @@ func writeAppBundle(appRoot, binPath, version, buildNo, arch string) error { if err := copyFile(binPath, dest, 0o755); err != nil { return err } - // Embed human-readable version next to the binary inside the .app - _ = os.WriteFile(filepath.Join(macOSDir, "VERSION"), []byte(version+"\n"), 0o644) return nil } -// findAsset locates a file under assets/ relative to cwd or the packmac binary. -func findAsset(name string) string { - candidates := []string{ - filepath.Join("assets", name), - } - if exe, err := os.Executable(); err == nil { - dir := filepath.Dir(exe) - candidates = append(candidates, - filepath.Join(dir, "..", "..", "assets", name), - filepath.Join(dir, "..", "assets", name), - filepath.Join(dir, "assets", name), - ) - } - if wd, err := os.Getwd(); err == nil { - candidates = append(candidates, filepath.Join(wd, "assets", name)) - } - for _, c := range candidates { - if st, err := os.Stat(c); err == nil && !st.IsDir() { - abs, err := filepath.Abs(c) - if err == nil { - return abs - } - return c - } - } - return "" -} - func signAppBundle(appRoot string) error { if _, err := exec.LookPath("codesign"); err != nil { return nil @@ -432,6 +394,34 @@ func mustStat(path string) os.FileInfo { return fi } +// findAsset locates a file under assets/ relative to cwd or the packmac binary. +func findAsset(name string) string { + candidates := []string{ + filepath.Join("assets", name), + } + if exe, err := os.Executable(); err == nil { + dir := filepath.Dir(exe) + candidates = append(candidates, + filepath.Join(dir, "..", "..", "assets", name), + filepath.Join(dir, "..", "assets", name), + filepath.Join(dir, "assets", name), + ) + } + if wd, err := os.Getwd(); err == nil { + candidates = append(candidates, filepath.Join(wd, "assets", name)) + } + for _, c := range candidates { + if st, err := os.Stat(c); err == nil && !st.IsDir() { + abs, err := filepath.Abs(c) + if err == nil { + return abs + } + return c + } + } + return "" +} + func copyFile(src, dst string, mode os.FileMode) error { in, err := os.Open(src) if err != nil { diff --git a/versioninfo.json b/versioninfo.json index 7c6f087..79a5408 100644 --- a/versioninfo.json +++ b/versioninfo.json @@ -4,13 +4,13 @@ "Major": 2, "Minor": 7, "Patch": 2, - "Build": 4 + "Build": 2 }, "ProductVersion": { "Major": 2, "Minor": 7, "Patch": 2, - "Build": 4 + "Build": 2 }, "FileFlagsMask": "3f", "FileFlags": "00", @@ -20,13 +20,13 @@ }, "StringFileInfo": { "CompanyName": "EvilFox", - "FileDescription": "Navis 2 \u2014 VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)", - "FileVersion": "2.7.2.4", + "FileDescription": "Navis 2 — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)", + "FileVersion": "2.7.2.2", "InternalName": "Navis", "LegalCopyright": "Copyright (c) EvilFox", "OriginalFilename": "Navis.exe", "ProductName": "Navis", - "ProductVersion": "2.7.2.4", + "ProductVersion": "2.7.2.2", "Comments": "Open-source VPN/proxy client. https://evilfox.win/" }, "VarFileInfo": {