Release 2.7.2.4: macOS app icon, glaze UI and auto build numbers.
Add green N AppIcon to the .app bundle, keep WKWebView GUI stable, and stamp builds via BaseVersion/BuildNumber. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 22 KiB |
+26
-9
@@ -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 /a BUILD_NUM+=1
|
||||
echo %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"
|
||||
|
||||
@@ -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 /a BUILD_NUM+=1
|
||||
echo %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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
4
|
||||
+20
-21
@@ -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)
|
||||
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, syscall.SIGINT, syscall.SIGTERM)
|
||||
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
|
||||
<-sig
|
||||
_ = mgr.Disconnect()
|
||||
_ = srv.Close()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = mgr.Disconnect()
|
||||
_ = srv.Close()
|
||||
w.Destroy()
|
||||
}()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
// Fallback: default browser
|
||||
return exec.Command("open", uiURL).Start()
|
||||
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
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
||||
2.7.2.4
|
||||
+1
@@ -0,0 +1 @@
|
||||
2.7.2.4
|
||||
Vendored
+3
-3
@@ -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",
|
||||
|
||||
Vendored
+3
-3
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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 @@
|
||||
<header class="top">
|
||||
<div class="logo" aria-hidden="true">
|
||||
<svg viewBox="0 0 32 32" fill="none">
|
||||
<path d="M8 26V6h4.2L20 18.4V6H24v20h-4.2L12 13.6V26H8z" fill="#f4fff9"/>
|
||||
<path d="M22.5 8.2l3.2-1.1-1.1 3.2-5.6 5.6 1.6 1.6 5.6-5.6 3.2-1.1-1.1 3.2" fill="#b8f0d8" opacity=".9"/>
|
||||
<!-- Bold N -->
|
||||
<path fill="#f4fff9" d="M8 25.2V7.6h4.4l7.4 11.2V7.6H24v17.6h-4.4L12.2 14V25.2H8z"/>
|
||||
<!-- Plug prongs on left stem -->
|
||||
<rect x="9.05" y="4.2" width="1.55" height="3.6" rx=".45" fill="#f4fff9"/>
|
||||
<rect x="11.55" y="4.2" width="1.55" height="3.6" rx=".45" fill="#f4fff9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="brand-wrap">
|
||||
<div class="brand-row">
|
||||
<h1 class="brand">Navis</h1>
|
||||
<span class="badge-ver">2.7</span>
|
||||
<span class="badge-ver" id="badgeVer">2.7.2</span>
|
||||
</div>
|
||||
<p class="tagline">Быстрый клиент · Naive · Hy2 · AWG · Xray</p>
|
||||
</div>
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Executable
+106
@@ -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 - <<PY
|
||||
from pathlib import Path
|
||||
p = Path('internal/update/version.go')
|
||||
t = p.read_text()
|
||||
import re
|
||||
t2 = re.sub(r'(var BuildNumber = ")[^"]*(")', r'\g<1>${BUILD_NUM}\2', t)
|
||||
p.write_text(t2)
|
||||
PY
|
||||
|
||||
# versioninfo.json (Windows metadata; also documents build)
|
||||
python3 - <<PY
|
||||
import json, re
|
||||
from pathlib import Path
|
||||
maj, minor, patch = [int(x) for x in "${BASE_VERSION}".split(".")[:3]]
|
||||
build = int("${BUILD_NUM}")
|
||||
full = "${FULL_VERSION}"
|
||||
p = Path('versioninfo.json')
|
||||
d = json.loads(p.read_text())
|
||||
d['FixedFileInfo']['FileVersion'] = {"Major": maj, "Minor": minor, "Patch": patch, "Build": build}
|
||||
d['FixedFileInfo']['ProductVersion'] = {"Major": maj, "Minor": minor, "Patch": patch, "Build": build}
|
||||
d['StringFileInfo']['FileVersion'] = f"{full}.0" if full.count('.')==3 else full
|
||||
d['StringFileInfo']['ProductVersion'] = d['StringFileInfo']['FileVersion']
|
||||
# Prefer 4-part a.b.c.d
|
||||
d['StringFileInfo']['FileVersion'] = f"{maj}.{minor}.{patch}.{build}"
|
||||
d['StringFileInfo']['ProductVersion'] = f"{maj}.{minor}.{patch}.{build}"
|
||||
p.write_text(json.dumps(d, indent=2) + "\n")
|
||||
print('versioninfo.json ->', 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 - <<PY
|
||||
import hashlib, json
|
||||
from pathlib import Path
|
||||
full = "${FULL_VERSION}"
|
||||
binp = Path('dist/navis-release/darwin-arm64/Navis')
|
||||
h = hashlib.sha256(binp.read_bytes()).hexdigest()
|
||||
for p in [Path('dist/update.json'), Path('dist/navis-release/update.json')]:
|
||||
if not p.exists():
|
||||
continue
|
||||
d = json.loads(p.read_text())
|
||||
d['version'] = full
|
||||
d['notes'] = f'Navis {full}'
|
||||
plats = d.setdefault('platforms', {})
|
||||
if 'darwin-arm64' in plats:
|
||||
plats['darwin-arm64']['sha256'] = h
|
||||
p.write_text(json.dumps(d, ensure_ascii=False, indent=2) + '\n')
|
||||
print('updated', p, '->', full)
|
||||
PY
|
||||
|
||||
echo ""
|
||||
echo "Done: Navis ${FULL_VERSION}"
|
||||
ls -lh "$OUT"
|
||||
file "$OUT/Navis.dmg"
|
||||
+60
-7
@@ -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 <Navis> -out <dir> [-version 1.4.1] [-arch arm64]")
|
||||
fmt.Fprintln(os.Stderr, "usage: packmac -bin <Navis> -out <dir> [-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(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
@@ -137,6 +154,7 @@ func writeAppBundle(appRoot, binPath, version, arch string) error {
|
||||
<key>CFBundleIdentifier</key><string>win.evilfox.navis</string>
|
||||
<key>CFBundleName</key><string>Navis</string>
|
||||
<key>CFBundleDisplayName</key><string>Navis</string>
|
||||
<key>CFBundleIconFile</key><string>AppIcon</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key><string>%s</string>
|
||||
<key>CFBundleVersion</key><string>%s</string>
|
||||
@@ -145,20 +163,55 @@ func writeAppBundle(appRoot, binPath, version, arch string) error {
|
||||
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>%s
|
||||
</dict>
|
||||
</plist>
|
||||
`, 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
|
||||
|
||||
+15
-5
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user