diff --git a/assets/navis-icon-1024.png b/assets/navis-icon-1024.png new file mode 100644 index 0000000..11a504b Binary files /dev/null and b/assets/navis-icon-1024.png differ diff --git a/assets/navis-icon.png b/assets/navis-icon.png index 1390b91..11a504b 100644 Binary files a/assets/navis-icon.png and b/assets/navis-icon.png differ diff --git a/assets/navis.icns b/assets/navis.icns new file mode 100644 index 0000000..5a17c15 Binary files /dev/null and b/assets/navis.icns differ diff --git a/assets/navis.ico b/assets/navis.ico index 91dfecc..a853848 100644 Binary files a/assets/navis.ico and b/assets/navis.ico differ diff --git a/build-macos.bat b/build-macos.bat index b5ca5e0..112de32 100644 --- a/build-macos.bat +++ b/build-macos.bat @@ -1,7 +1,21 @@ @echo off -setlocal +setlocal EnableDelayedExpansion 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 @@ -9,16 +23,18 @@ mkdir "dist\navis-release\darwin-universal" 2>nul set CGO_ENABLED=0 set GOOS=darwin set GOARCH=arm64 -go build -ldflags="-s -w" -o "dist\navis-release\darwin-arm64\Navis" .\cmd\vpnapp +go build -ldflags="%LDFLAGS%" -trimpath -o "dist\navis-release\darwin-arm64\Navis" .\cmd\vpnapp if errorlevel 1 exit /b 1 -go build -ldflags="-s -w" -o "dist\navis-release\darwin-arm64\Navis-cli" .\cmd\vpnclient +go build -ldflags="%LDFLAGS%" -trimpath -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="-s -w" -o "dist\navis-release\darwin-amd64\Navis" .\cmd\vpnapp +go build -ldflags="%LDFLAGS%" -trimpath -o "dist\navis-release\darwin-amd64\Navis" .\cmd\vpnapp if errorlevel 1 exit /b 1 -go build -ldflags="-s -w" -o "dist\navis-release\darwin-amd64\Navis-cli" .\cmd\vpnclient +go build -ldflags="%LDFLAGS%" -trimpath -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= @@ -30,18 +46,19 @@ 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 2.7.1 -arch arm64 +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 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version 2.7.1 -arch amd64 +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 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-universal\Navis" -out "dist\navis-release\darwin-universal" -version 2.7.1 -arch universal +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 if errorlevel 1 exit /b 1 -echo Built Mac GUI + CLI: +echo Built Mac GUI + CLI %FULL_VERSION%: 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 9374f1e..761197b 100644 --- a/build.bat +++ b/build.bat @@ -1,7 +1,19 @@ @echo off -setlocal +setlocal EnableDelayedExpansion 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 @@ -14,16 +26,21 @@ 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="-H windowsgui -s -w" -trimpath -o Navis.exe ./cmd/vpnapp +go build -ldflags="%LDFLAGS%" -trimpath -o Navis.exe ./cmd/vpnapp if errorlevel 1 exit /b 1 -go build -ldflags="-s -w" -trimpath -o vpnclient.exe ./cmd/vpnclient +go build -ldflags="-s -w -X vpnclient/internal/update.BuildNumber=%BUILD_NUM%" -trimpath -o vpnclient.exe ./cmd/vpnclient if errorlevel 1 exit /b 1 +echo %FULL_VERSION%> Navis.version + echo. -echo Done: +echo Done: Navis %FULL_VERSION% echo Navis.exe echo vpnclient.exe +echo Navis.version echo assets\navis.ico endlocal diff --git a/buildnum.txt b/buildnum.txt new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/buildnum.txt @@ -0,0 +1 @@ +4 diff --git a/cmd/vpnapp/main_darwin.go b/cmd/vpnapp/main_darwin.go index 70e111f..6ba46b8 100644 --- a/cmd/vpnapp/main_darwin.go +++ b/cmd/vpnapp/main_darwin.go @@ -10,10 +10,11 @@ import ( "os" "os/exec" "os/signal" - "path/filepath" "syscall" "time" + "github.com/crgimenes/glaze" + "vpnclient/internal/apphost" "vpnclient/internal/config" "vpnclient/internal/core" @@ -67,30 +68,31 @@ func main() { }() go a.AutoCheckUpdate() - if err := openAppWindow(uiURL); err != nil { - log.Printf("open UI: %v — откройте вручную: %s", err, uiURL) - fmt.Println(uiURL) - } else { - log.Printf("Navis UI: %s", uiURL) - } - - sig := make(chan os.Signal, 1) - signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) - <-sig - _ = mgr.Disconnect() - _ = srv.Close() -} - -func openAppWindow(uiURL string) error { - // Prefer Chromium-based --app window (looks like a desktop client). - for _, app := range []string{"Google Chrome", "Chromium", "Microsoft Edge", "Brave Browser", "Arc"} { - cmd := exec.Command("open", "-na", app, "--args", "--app="+uiURL, "--new-window") - if err := cmd.Start(); err == nil { - return nil + w, err := glaze.New(false) + if err != nil || w == nil { + log.Printf("webview unavailable (%v); opening default browser", err) + if err2 := exec.Command("open", uiURL).Run(); err2 != nil { + fatalf("Не удалось открыть интерфейс:\n%v\n%v\n%s", err, err2, uiURL) } + log.Printf("Navis UI: %s", uiURL) + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGTERM) + <-sig + _ = mgr.Disconnect() + _ = srv.Close() + return } - // Fallback: default browser - return exec.Command("open", uiURL).Start() + defer func() { + _ = mgr.Disconnect() + _ = srv.Close() + w.Destroy() + }() + + w.SetTitle("Navis 2") + w.SetSize(500, 900, glaze.HintNone) + w.Navigate(uiURL) + log.Printf("Navis UI: %s", uiURL) + w.Run() } func fatalf(format string, args ...any) { @@ -99,6 +101,3 @@ func fatalf(format string, args ...any) { _ = exec.Command("osascript", "-e", fmt.Sprintf(`display alert "Navis" message %q`, msg)).Run() os.Exit(1) } - -// Ensure we keep a reference for potential future dock icon path helpers. -var _ = filepath.Separator diff --git a/dist/navis-release/darwin-arm64/Navis b/dist/navis-release/darwin-arm64/Navis index 57cd02a..b60126e 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 67ee051..5ef1043 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 5ad25a4..bfe1132 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 8e0bce2..a75b0a6 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 new file mode 100644 index 0000000..388e070 --- /dev/null +++ b/dist/navis-release/darwin-arm64/Navis.version @@ -0,0 +1 @@ +2.7.2.4 diff --git a/dist/navis-release/darwin-arm64/VERSION b/dist/navis-release/darwin-arm64/VERSION new file mode 100644 index 0000000..388e070 --- /dev/null +++ b/dist/navis-release/darwin-arm64/VERSION @@ -0,0 +1 @@ +2.7.2.4 diff --git a/dist/navis-release/update.json b/dist/navis-release/update.json index 38c187e..63ce6f3 100644 --- a/dist/navis-release/update.json +++ b/dist/navis-release/update.json @@ -1,6 +1,6 @@ { - "version": "2.7.1", - "notes": "macOS: UDZO DMG, Application Support cores path, naiveproxy asset name fix", + "version": "2.7.2.4", + "notes": "Navis 2.7.2.4", "platform": "windows-amd64", "os": "windows", "arch": "amd64", @@ -16,7 +16,7 @@ }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "8951c351b224d8a13e2772b6ee080d7bee3285ac162dd20c0855a8653a569ec5", + "sha256": "4f1f34f57ed7cd588f9d62f24d074041be3f3b1504f18e8203c953536236c381", "os": "darwin", "arch": "arm64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", diff --git a/dist/update.json b/dist/update.json index 38c187e..63ce6f3 100644 --- a/dist/update.json +++ b/dist/update.json @@ -1,6 +1,6 @@ { - "version": "2.7.1", - "notes": "macOS: UDZO DMG, Application Support cores path, naiveproxy asset name fix", + "version": "2.7.2.4", + "notes": "Navis 2.7.2.4", "platform": "windows-amd64", "os": "windows", "arch": "amd64", @@ -16,7 +16,7 @@ }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "8951c351b224d8a13e2772b6ee080d7bee3285ac162dd20c0855a8653a569ec5", + "sha256": "4f1f34f57ed7cd588f9d62f24d074041be3f3b1504f18e8203c953536236c381", "os": "darwin", "arch": "arm64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", diff --git a/go.mod b/go.mod index 5c0121b..f896ae2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module vpnclient -go 1.25.0 +go 1.26.5 require ( github.com/amnezia-vpn/amneziawg-go v0.2.19 @@ -12,7 +12,9 @@ require ( require ( github.com/anchore/go-lzo v0.1.0 // indirect + github.com/crgimenes/glaze v0.0.33 // indirect github.com/djherbis/times v1.6.0 // indirect + github.com/ebitengine/purego v0.10.1 // indirect github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/uuid v1.6.0 // indirect diff --git a/go.sum b/go.sum index 64e466e..77ce956 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,16 @@ github.com/amnezia-vpn/amneziawg-go v0.2.19 h1:l3rOmrA4o5z38kpgnA5iSk1yOm7Cv3Aaf github.com/amnezia-vpn/amneziawg-go v0.2.19/go.mod h1:aMgOk9MuX0xI7b5TKAYp8pLM54RlXcOPzDvYw3YEO5A= github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs= github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk= +github.com/crgimenes/glaze v0.0.33 h1:XZm2cFTSFSY7UarC4w/ziCMJ7Zwkce5Dh1NaO1Koj5Q= +github.com/crgimenes/glaze v0.0.33/go.mod h1:ZuCIST0F5U6wJLw5ZtqfcTIQA7LI/m2MHv7iMOBmu6U= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/diskfs/go-diskfs v1.9.4 h1:0j2d7eG4IjyxL6+ChWbDPocdBCF6HQ4HBWU2WDYWVnc= github.com/diskfs/go-diskfs v1.9.4/go.mod h1:TePJORO83Adh5pb2SqsxAwaP0fofFxKLkxctiS/9OQc= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= +github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY= +github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57 h1:x5yxNrq8XffV/OoNUeFPM6hxHVi5OTspSTBxr/9pemg= github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= diff --git a/internal/appui/index.html b/internal/appui/index.html index e0ceb54..d365627 100644 --- a/internal/appui/index.html +++ b/internal/appui/index.html @@ -114,7 +114,7 @@ font-size: .68rem; font-weight: 700; letter-spacing: .04em; - text-transform: uppercase; + text-transform: none; color: var(--accent-deep); background: var(--accent-soft); border: 1px solid rgba(13,138,102,.18); @@ -718,14 +718,17 @@

Navis

- 2.7 + 2.7.2

Быстрый клиент · Naive · Hy2 · AWG · Xray

@@ -1169,6 +1172,8 @@ function renderUpdate(u, version) { verLabel.textContent = "Navis v" + (version || "?"); + const badge = $("badgeVer"); + if (badge && version) badge.textContent = version; if (!u) { updateBanner.classList.remove("show"); return; diff --git a/internal/update/update.go b/internal/update/update.go index d278be1..63c5936 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -16,9 +16,6 @@ import ( "time" ) -// CurrentVersion is the shipped client version. -const CurrentVersion = "2.7.1" - // 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" diff --git a/internal/update/version.go b/internal/update/version.go new file mode 100644 index 0000000..3753d50 --- /dev/null +++ b/internal/update/version.go @@ -0,0 +1,31 @@ +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 new file mode 100755 index 0000000..b305b9f --- /dev/null +++ b/scripts/build-macos-arm64.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# Build Navis GUI for Apple Silicon (arm64) with auto-incremented build number. +set -euo pipefail +cd "$(dirname "$0")/.." + +BASE_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) +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']) +PY + +OUT="dist/navis-release/darwin-arm64" +mkdir -p "$OUT" +LDFLAGS="-s -w -X vpnclient/internal/update.BuildNumber=${BUILD_NUM}" + +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" + +if command -v codesign >/dev/null 2>&1; then + codesign -s - --force "$OUT/Navis" + codesign -s - --force "$OUT/Navis-cli" +fi + +go build -o tools/packmac/packmac ./tools/packmac +tools/packmac/packmac \ + -bin "$OUT/Navis" \ + -out "$OUT" \ + -version "$FULL_VERSION" \ + -build "$BUILD_NUM" \ + -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" + +# update.json +python3 - <', full) +PY + +echo "" +echo "Done: Navis ${FULL_VERSION}" +ls -lh "$OUT" +file "$OUT/Navis.dmg" diff --git a/tools/packmac/main.go b/tools/packmac/main.go index e1a0dd6..909d9bb 100644 --- a/tools/packmac/main.go +++ b/tools/packmac/main.go @@ -19,20 +19,33 @@ 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") + 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)") arch := flag.String("arch", "arm64", "arch label for volume name") flag.Parse() if *bin == "" || *outDir == "" { - fmt.Fprintln(os.Stderr, "usage: packmac -bin -out [-version 1.4.1] [-arch arm64]") + fmt.Fprintln(os.Stderr, "usage: packmac -bin -out [-version 2.7.2.3] [-build 3] [-arch arm64]") os.Exit(2) } - if err := run(*bin, *outDir, *version, *arch); err != nil { + buildNo := *build + if buildNo == "" { + buildNo = lastVersionSegment(*version) + } + if err := run(*bin, *outDir, *version, buildNo, *arch); err != nil { fmt.Fprintf(os.Stderr, "packmac: %v\n", err) os.Exit(1) } } -func run(binPath, outDir, version, arch string) error { +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 { if err := os.MkdirAll(outDir, 0o755); err != nil { return err } @@ -43,7 +56,7 @@ func run(binPath, outDir, version, arch string) error { defer os.RemoveAll(stage) appRoot := filepath.Join(stage, "Navis.app") - if err := writeAppBundle(appRoot, binPath, version, arch); err != nil { + if err := writeAppBundle(appRoot, binPath, version, buildNo, arch); err != nil { return err } if err := signAppBundle(appRoot); err != nil { @@ -123,11 +136,15 @@ func writeHdiutilDMG(dmgPath, stageDir, volume string) error { return nil } -func writeAppBundle(appRoot, binPath, version, arch string) error { +func writeAppBundle(appRoot, binPath, version, buildNo, arch string) error { macOSDir := filepath.Join(appRoot, "Contents", "MacOS") + resDir := filepath.Join(appRoot, "Contents", "Resources") if err := os.MkdirAll(macOSDir, 0o755); err != nil { return err } + if err := os.MkdirAll(resDir, 0o755); err != nil { + return err + } archPriority := architecturePriorityXML(arch) plist := fmt.Sprintf(` @@ -137,6 +154,7 @@ func writeAppBundle(appRoot, binPath, version, arch string) error { CFBundleIdentifierwin.evilfox.navis CFBundleNameNavis CFBundleDisplayNameNavis + CFBundleIconFileAppIcon CFBundlePackageTypeAPPL CFBundleShortVersionString%s CFBundleVersion%s @@ -145,20 +163,55 @@ func writeAppBundle(appRoot, binPath, version, arch string) error { CFBundleInfoDictionaryVersion6.0%s -`, version, version, archPriority) +`, version, buildNo, archPriority) if err := os.WriteFile(filepath.Join(appRoot, "Contents", "Info.plist"), []byte(plist), 0o644); err != nil { return err } if err := os.WriteFile(filepath.Join(appRoot, "Contents", "PkgInfo"), []byte("APPL????"), 0o644); err != nil { return err } + if icns := findAsset("navis.icns"); icns != "" { + if err := copyFile(icns, filepath.Join(resDir, "AppIcon.icns"), 0o644); err != nil { + return fmt.Errorf("app icon: %w", err) + } + } dest := filepath.Join(macOSDir, "Navis") 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 diff --git a/versioninfo.json b/versioninfo.json index 844ecaf..7c6f087 100644 --- a/versioninfo.json +++ b/versioninfo.json @@ -1,7 +1,17 @@ { "FixedFileInfo": { - "FileVersion": { "Major": 2, "Minor": 7, "Patch": 0, "Build": 0 }, - "ProductVersion": { "Major": 2, "Minor": 7, "Patch": 0, "Build": 0 }, + "FileVersion": { + "Major": 2, + "Minor": 7, + "Patch": 2, + "Build": 4 + }, + "ProductVersion": { + "Major": 2, + "Minor": 7, + "Patch": 2, + "Build": 4 + }, "FileFlagsMask": "3f", "FileFlags": "00", "FileOS": "40004", @@ -10,13 +20,13 @@ }, "StringFileInfo": { "CompanyName": "EvilFox", - "FileDescription": "Navis 2 — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)", - "FileVersion": "2.7.1.0", + "FileDescription": "Navis 2 \u2014 VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)", + "FileVersion": "2.7.2.4", "InternalName": "Navis", "LegalCopyright": "Copyright (c) EvilFox", "OriginalFilename": "Navis.exe", "ProductName": "Navis", - "ProductVersion": "2.7.1.0", + "ProductVersion": "2.7.2.4", "Comments": "Open-source VPN/proxy client. https://evilfox.win/" }, "VarFileInfo": {