diff --git a/README.md b/README.md index 17839a0..ffa529f 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ ## Платформы -| OS | Артефакт | UI | +| OS | Артефакты | UI | |----|----------|----| -| Windows amd64 | `dist/navis-release/Navis.exe` | GUI (WebView2) | -| macOS arm64 | `dist/navis-release/darwin-arm64/Navis` | CLI | -| macOS amd64 | `dist/navis-release/darwin-amd64/Navis` | CLI | +| Windows amd64 | `Navis.exe` | GUI (WebView2) | +| macOS arm64 | `Navis` · **`Navis.dmg`** · `Navis.app.zip` | CLI / .app | +| macOS amd64 | `Navis` · **`Navis.dmg`** · `Navis.app.zip` | CLI / .app | ## Скачать macOS прямо с git @@ -25,7 +25,55 @@ uname -m - `arm64` → Apple Silicon (M1/M2/M3/M4) → папка **darwin-arm64** - `x86_64` → Intel → папка **darwin-amd64** -### 2. Скачайте бинарник одной командой +### 2a. Скачать DMG (удобнее) + +**Apple Silicon:** + +```bash +cd ~/Downloads +curl -fL -o Navis.dmg \ + "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg" +open Navis.dmg +``` + +**Intel:** + +```bash +cd ~/Downloads +curl -fL -o Navis.dmg \ + "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg" +open Navis.dmg +``` + +Откроется диск с `Navis.app` — перетащите в **Программы** (Applications). + +Если Gatekeeper ругается: + +```bash +xattr -dr com.apple.quarantine /Applications/Navis.app +``` + +Запуск CLI из .app: + +```bash +/Applications/Navis.app/Contents/MacOS/Navis init +/Applications/Navis.app/Contents/MacOS/Navis install-core +/Applications/Navis.app/Contents/MacOS/Navis connect +``` + +Альтернатива DMG — zip с тем же `.app`: + +```bash +# arm64 +curl -fL -o Navis.app.zip \ + "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.app.zip" +# amd64 +curl -fL -o Navis.app.zip \ + "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.app.zip" +unzip Navis.app.zip +``` + +### 2b. Скачать CLI-бинарник одной командой **Apple Silicon (arm64):** @@ -51,7 +99,7 @@ chmod +x Navis ./Navis check-update ``` -### 3. Первый запуск +### 3. Первый запуск (CLI) ```bash cd ~/Navis @@ -82,10 +130,10 @@ cd ~/Navis “Navis” cannot be opened because it is from an unidentified developer ``` -Снимите quarantine и разрешите в Настройках: - ```bash xattr -dr com.apple.quarantine ~/Navis/Navis +# или для .app: +xattr -dr com.apple.quarantine /Applications/Navis.app ``` Затем: **Системные настройки → Конфиденциальность и безопасность → Всё равно открыть**. @@ -99,15 +147,15 @@ cd ~/Navis # закройте процесс Navis — бинарник подменится сам ``` -Или скачайте файл заново теми же `curl` из шага 2. +Или скачайте DMG/бинарник заново. Прямые ссылки (raw): -| Архитектура | URL | -|-------------|-----| -| arm64 | https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis | -| amd64 | https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis | -| манифест | https://git.evilfox.cc/test2/navi/raw/branch/main/dist/update.json | +| Архитектура | CLI | DMG | ZIP (.app) | +|-------------|-----|-----|------------| +| arm64 | [Navis](https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis) | [Navis.dmg](https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg) | [Navis.app.zip](https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.app.zip) | +| amd64 | [Navis](https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis) | [Navis.dmg](https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg) | [Navis.app.zip](https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.app.zip) | +| манифест | [update.json](https://git.evilfox.cc/test2/navi/raw/branch/main/dist/update.json) | | | ## Сборка Windows diff --git a/build-macos.bat b/build-macos.bat index 3c236f2..33ba068 100644 --- a/build-macos.bat +++ b/build-macos.bat @@ -15,7 +15,19 @@ set GOARCH=amd64 go build -ldflags="-s -w" -o "dist\navis-release\darwin-amd64\Navis" .\cmd\vpnclient if errorlevel 1 exit /b 1 +set GOOS= +set GOARCH= +set CGO_ENABLED= + +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 1.4.1 -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 1.4.1 -arch amd64 +if errorlevel 1 exit /b 1 + echo Built: -dir /b "dist\navis-release\darwin-arm64\Navis" -dir /b "dist\navis-release\darwin-amd64\Navis" +dir "dist\navis-release\darwin-arm64" +dir "dist\navis-release\darwin-amd64" endlocal diff --git a/dist/navis-release/Navis.exe b/dist/navis-release/Navis.exe index 610b94d..21e1b6b 100644 Binary files a/dist/navis-release/Navis.exe and b/dist/navis-release/Navis.exe differ diff --git a/dist/navis-release/darwin-amd64/Navis b/dist/navis-release/darwin-amd64/Navis index a011c68..7dab97b 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.app.zip b/dist/navis-release/darwin-amd64/Navis.app.zip new file mode 100644 index 0000000..c75f35b Binary files /dev/null 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 new file mode 100644 index 0000000..84f56bd Binary files /dev/null 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 0d7c3ca..d52b58d 100644 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.app.zip b/dist/navis-release/darwin-arm64/Navis.app.zip new file mode 100644 index 0000000..8d4134c Binary files /dev/null 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 new file mode 100644 index 0000000..480c003 Binary files /dev/null and b/dist/navis-release/darwin-arm64/Navis.dmg differ diff --git a/dist/navis-release/update.json b/dist/navis-release/update.json index b4bb977..632cad4 100644 --- a/dist/navis-release/update.json +++ b/dist/navis-release/update.json @@ -1,30 +1,38 @@ { - "version": "1.4.0", - "notes": "macOS CLI (arm64/amd64), multi-platform updates, networksetup proxy, naive tar.xz", + "version": "1.4.1", + "notes": "macOS DMG + Navis.app.zip в релизе (arm64/amd64)", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "110ab8f5806f2635b39ec586d8218120d0041a82212715530ae8c3838078bf11", + "sha256": "4217542dfa49a60d522295267b9bd7f3a838b2917b68d9ee59041abecf149e09", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "110ab8f5806f2635b39ec586d8218120d0041a82212715530ae8c3838078bf11", + "sha256": "4217542dfa49a60d522295267b9bd7f3a838b2917b68d9ee59041abecf149e09", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "4ce2b429fe4c8e8e1fdf776150003e42e4452e9ecf95cb55a714f84884717fd6", + "sha256": "a3bc2bcc256842d3914c44385a5fb9b61e691be2c19a9c283bc2099fcc48af9a", "os": "darwin", - "arch": "arm64" + "arch": "arm64", + "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", + "dmg_sha256": "a49584770e7453e7046ba80a4da8ac84a34797e3a8e4eb307a3d572d1fc95b59", + "zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.app.zip", + "zip_sha256": "436c8c0382bf4491d288f6899d1a8fb0f7cd86b53063801f9dbc03f29a92046d" }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "a91bc03c7c68d896c42bb3520010e96191c12a0e95e2b9c2582dd2732637d9af", + "sha256": "a1471b447e481e3fe61a65bb66c8633facd968087907e561584fd845842805fb", "os": "darwin", - "arch": "amd64" + "arch": "amd64", + "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", + "dmg_sha256": "eca2127f956959c1a4567dece779bfcd4bd414d2f63f430c24b133456fa5c56a", + "zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.app.zip", + "zip_sha256": "de039f591032dbcf69e81499074cf7f51b80c83f08958521df9b8f873d2fe7be" } } } diff --git a/dist/update.json b/dist/update.json index b4bb977..632cad4 100644 --- a/dist/update.json +++ b/dist/update.json @@ -1,30 +1,38 @@ { - "version": "1.4.0", - "notes": "macOS CLI (arm64/amd64), multi-platform updates, networksetup proxy, naive tar.xz", + "version": "1.4.1", + "notes": "macOS DMG + Navis.app.zip в релизе (arm64/amd64)", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "110ab8f5806f2635b39ec586d8218120d0041a82212715530ae8c3838078bf11", + "sha256": "4217542dfa49a60d522295267b9bd7f3a838b2917b68d9ee59041abecf149e09", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "110ab8f5806f2635b39ec586d8218120d0041a82212715530ae8c3838078bf11", + "sha256": "4217542dfa49a60d522295267b9bd7f3a838b2917b68d9ee59041abecf149e09", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "4ce2b429fe4c8e8e1fdf776150003e42e4452e9ecf95cb55a714f84884717fd6", + "sha256": "a3bc2bcc256842d3914c44385a5fb9b61e691be2c19a9c283bc2099fcc48af9a", "os": "darwin", - "arch": "arm64" + "arch": "arm64", + "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", + "dmg_sha256": "a49584770e7453e7046ba80a4da8ac84a34797e3a8e4eb307a3d572d1fc95b59", + "zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.app.zip", + "zip_sha256": "436c8c0382bf4491d288f6899d1a8fb0f7cd86b53063801f9dbc03f29a92046d" }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "a91bc03c7c68d896c42bb3520010e96191c12a0e95e2b9c2582dd2732637d9af", + "sha256": "a1471b447e481e3fe61a65bb66c8633facd968087907e561584fd845842805fb", "os": "darwin", - "arch": "amd64" + "arch": "amd64", + "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", + "dmg_sha256": "eca2127f956959c1a4567dece779bfcd4bd414d2f63f430c24b133456fa5c56a", + "zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.app.zip", + "zip_sha256": "de039f591032dbcf69e81499074cf7f51b80c83f08958521df9b8f873d2fe7be" } } } diff --git a/go.mod b/go.mod index 83f0e4e..d491eda 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,21 @@ module vpnclient go 1.25.0 require ( + github.com/diskfs/go-diskfs v1.9.4 github.com/jchv/go-webview2 v0.0.0-20260205173254-56598839c808 golang.org/x/image v0.44.0 - golang.org/x/sys v0.33.0 + golang.org/x/sys v0.43.0 ) -require github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect +require ( + github.com/anchore/go-lzo v0.1.0 // indirect + github.com/djherbis/times v1.6.0 // indirect + github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect + github.com/klauspost/compress v1.18.5 // indirect + github.com/pierrec/lz4/v4 v4.1.26 // indirect + github.com/pkg/xattr v0.4.12 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect + github.com/ulikunitz/xz v0.5.15 // indirect +) diff --git a/go.sum b/go.sum index 002bbd5..38c814d 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,44 @@ +github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs= +github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk= +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/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= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jchv/go-webview2 v0.0.0-20260205173254-56598839c808 h1:ftnsTqIUH57XQEF+PnXX9++nlHCzdkuB5zbWyMMruZo= github.com/jchv/go-webview2 v0.0.0-20260205173254-56598839c808/go.mod h1:rWifBlzkgrvd7zUqlfq91sWt3473OikgnglnIILx/Jo= github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ= github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= +github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= +github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM= +github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I= golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210218145245-beda7e5e158e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/subscription/fetch.go b/internal/subscription/fetch.go index b05171e..632d144 100644 --- a/internal/subscription/fetch.go +++ b/internal/subscription/fetch.go @@ -39,7 +39,7 @@ func Fetch(ctx context.Context, rawURL string) ([]Item, error) { if err != nil { return nil, err } - req.Header.Set("User-Agent", "Navis/1.4.0") + req.Header.Set("User-Agent", "Navis/1.4.1") client := &http.Client{Timeout: 45 * time.Second} resp, err := client.Do(req) if err != nil { diff --git a/internal/update/update.go b/internal/update/update.go index 6fddc4d..92acb0c 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -17,17 +17,21 @@ import ( ) // CurrentVersion is the shipped client version. -const CurrentVersion = "1.4.0" +const CurrentVersion = "1.4.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" // PlatformAsset is one OS/arch binary in the feed. type PlatformAsset struct { - URL string `json:"url"` - SHA256 string `json:"sha256,omitempty"` - OS string `json:"os,omitempty"` - Arch string `json:"arch,omitempty"` + URL string `json:"url"` + SHA256 string `json:"sha256,omitempty"` + OS string `json:"os,omitempty"` + Arch string `json:"arch,omitempty"` + DmgURL string `json:"dmg_url,omitempty"` + DmgSHA256 string `json:"dmg_sha256,omitempty"` + ZipURL string `json:"zip_url,omitempty"` + ZipSHA256 string `json:"zip_sha256,omitempty"` } // Manifest describes a remote release (multi-platform + legacy single URL). diff --git a/server/update.json b/server/update.json index b4bb977..632cad4 100644 --- a/server/update.json +++ b/server/update.json @@ -1,30 +1,38 @@ { - "version": "1.4.0", - "notes": "macOS CLI (arm64/amd64), multi-platform updates, networksetup proxy, naive tar.xz", + "version": "1.4.1", + "notes": "macOS DMG + Navis.app.zip в релизе (arm64/amd64)", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "110ab8f5806f2635b39ec586d8218120d0041a82212715530ae8c3838078bf11", + "sha256": "4217542dfa49a60d522295267b9bd7f3a838b2917b68d9ee59041abecf149e09", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "110ab8f5806f2635b39ec586d8218120d0041a82212715530ae8c3838078bf11", + "sha256": "4217542dfa49a60d522295267b9bd7f3a838b2917b68d9ee59041abecf149e09", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "4ce2b429fe4c8e8e1fdf776150003e42e4452e9ecf95cb55a714f84884717fd6", + "sha256": "a3bc2bcc256842d3914c44385a5fb9b61e691be2c19a9c283bc2099fcc48af9a", "os": "darwin", - "arch": "arm64" + "arch": "arm64", + "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", + "dmg_sha256": "a49584770e7453e7046ba80a4da8ac84a34797e3a8e4eb307a3d572d1fc95b59", + "zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.app.zip", + "zip_sha256": "436c8c0382bf4491d288f6899d1a8fb0f7cd86b53063801f9dbc03f29a92046d" }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "a91bc03c7c68d896c42bb3520010e96191c12a0e95e2b9c2582dd2732637d9af", + "sha256": "a1471b447e481e3fe61a65bb66c8633facd968087907e561584fd845842805fb", "os": "darwin", - "arch": "amd64" + "arch": "amd64", + "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", + "dmg_sha256": "eca2127f956959c1a4567dece779bfcd4bd414d2f63f430c24b133456fa5c56a", + "zip_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.app.zip", + "zip_sha256": "de039f591032dbcf69e81499074cf7f51b80c83f08958521df9b8f873d2fe7be" } } } diff --git a/tools/packmac/main.go b/tools/packmac/main.go new file mode 100644 index 0000000..df5bd20 --- /dev/null +++ b/tools/packmac/main.go @@ -0,0 +1,354 @@ +package main + +import ( + "archive/zip" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + diskfs "github.com/diskfs/go-diskfs" + "github.com/diskfs/go-diskfs/disk" + "github.com/diskfs/go-diskfs/filesystem" + "github.com/diskfs/go-diskfs/filesystem/iso9660" +) + +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") + 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]") + os.Exit(2) + } + if err := run(*bin, *outDir, *version, *arch); err != nil { + fmt.Fprintf(os.Stderr, "packmac: %v\n", err) + os.Exit(1) + } +} + +func run(binPath, outDir, version, arch string) error { + if err := os.MkdirAll(outDir, 0o755); err != nil { + return err + } + stage, err := os.MkdirTemp("", "navis-packmac-*") + if err != nil { + return err + } + defer os.RemoveAll(stage) + + appRoot := filepath.Join(stage, "Navis.app") + if err := writeAppBundle(appRoot, binPath, version); err != nil { + return err + } + readme := filepath.Join(stage, "README.txt") + if err := os.WriteFile(readme, []byte(readmeText(version, arch)), 0o644); err != nil { + return err + } + + zipPath := filepath.Join(outDir, "Navis.app.zip") + if err := zipDir(zipPath, stage, []string{"Navis.app", "README.txt"}); err != nil { + return fmt.Errorf("zip: %w", err) + } + fmt.Println("wrote", zipPath) + + dmgPath := filepath.Join(outDir, "Navis.dmg") + if err := writeDMG(dmgPath, appRoot, readme, "Navis-"+arch); err != nil { + return fmt.Errorf("dmg: %w", err) + } + fmt.Println("wrote", dmgPath) + return nil +} + +func writeAppBundle(appRoot, binPath, version string) error { + macOSDir := filepath.Join(appRoot, "Contents", "MacOS") + if err := os.MkdirAll(macOSDir, 0o755); err != nil { + return err + } + plist := fmt.Sprintf(` + + + + CFBundleExecutableNavis + CFBundleIdentifierwin.evilfox.navis + CFBundleNameNavis + CFBundlePackageTypeAPPL + CFBundleShortVersionString%s + CFBundleVersion%s + LSMinimumSystemVersion11.0 + CFBundleInfoDictionaryVersion6.0 + + +`, version, version) + if err := os.WriteFile(filepath.Join(appRoot, "Contents", "Info.plist"), []byte(plist), 0o644); err != nil { + return err + } + dest := filepath.Join(macOSDir, "Navis") + if err := copyFile(binPath, dest, 0o755); err != nil { + return err + } + return nil +} + +func writeDMG(dmgPath, appRoot, readmePath, volume string) error { + _ = os.Remove(dmgPath) + appSize, err := dirSize(appRoot) + if err != nil { + return err + } + readmeSize, err := fileSize(readmePath) + if err != nil { + return err + } + need := appSize + readmeSize + 4*1024*1024 + if need < 12*1024*1024 { + need = 12 * 1024 * 1024 + } + // round up to 2048 + need = ((need + 2047) / 2048) * 2048 + + mydisk, err := diskfs.Create(dmgPath, need, diskfs.SectorSizeDefault) + if err != nil { + return err + } + mydisk.LogicalBlocksize = 2048 + fs, err := mydisk.CreateFilesystem(disk.FilesystemSpec{ + Partition: 0, + FSType: filesystem.TypeISO9660, + VolumeLabel: sanitizeVol(volume), + }) + if err != nil { + return err + } + + if err := copyTreeToISO(fs, appRoot, "/Navis.app"); err != nil { + _ = fs.Close() + return err + } + if err := writeISOFile(fs, "/README.txt", readmePath, 0o644); err != nil { + _ = fs.Close() + return err + } + + iso, ok := fs.(*iso9660.FileSystem) + if !ok { + _ = fs.Close() + return fmt.Errorf("not iso9660") + } + if err := iso.Finalize(iso9660.FinalizeOptions{ + RockRidge: true, + Joliet: true, + DeepDirectories: true, + VolumeIdentifier: sanitizeVol(volume), + PublisherIdentifier: "Navis", + }); err != nil { + return err + } + return fs.Close() +} + +func copyTreeToISO(fs filesystem.FileSystem, srcRoot, dstRoot string) error { + return filepath.Walk(srcRoot, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + rel, err := filepath.Rel(srcRoot, path) + if err != nil { + return err + } + rel = filepath.ToSlash(rel) + dst := dstRoot + if rel != "." { + dst = dstRoot + "/" + rel + } + if info.IsDir() { + if dst == dstRoot { + return fs.Mkdir(dst) + } + return fs.Mkdir(dst) + } + mode := info.Mode() + if mode&0o111 != 0 || strings.HasSuffix(rel, "MacOS/Navis") || strings.HasSuffix(rel, "MacOS\\Navis") { + mode = 0o755 + } else { + mode = 0o644 + } + return writeISOFile(fs, dst, path, mode) + }) +} + +func writeISOFile(fs filesystem.FileSystem, dst, src string, mode os.FileMode) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + rw, err := fs.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC) + if err != nil { + return err + } + if _, err := io.Copy(rw, in); err != nil { + _ = rw.Close() + return err + } + if err := rw.Close(); err != nil { + return err + } + if err := fs.Chmod(dst, mode); err != nil { + _ = err // best-effort + } + return nil +} + +func zipDir(zipPath, stage string, entries []string) error { + _ = os.Remove(zipPath) + f, err := os.Create(zipPath) + if err != nil { + return err + } + defer f.Close() + zw := zip.NewWriter(f) + defer zw.Close() + + for _, name := range entries { + root := filepath.Join(stage, name) + info, err := os.Stat(root) + if err != nil { + return err + } + if !info.IsDir() { + if err := addZipFile(zw, root, name, 0o644); err != nil { + return err + } + continue + } + err = filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + rel, err := filepath.Rel(stage, path) + if err != nil { + return err + } + rel = filepath.ToSlash(rel) + if fi.IsDir() { + _, err := zw.Create(rel + "/") + return err + } + mode := os.FileMode(0o644) + if strings.Contains(rel, "Contents/MacOS/") { + mode = 0o755 + } + return addZipFile(zw, path, rel, mode) + }) + if err != nil { + return err + } + } + return nil +} + +func addZipFile(zw *zip.Writer, path, name string, mode os.FileMode) error { + h, err := zip.FileInfoHeader(mustStat(path)) + if err != nil { + return err + } + h.Name = name + h.Method = zip.Deflate + h.SetMode(mode) + w, err := zw.CreateHeader(h) + if err != nil { + return err + } + in, err := os.Open(path) + if err != nil { + return err + } + defer in.Close() + _, err = io.Copy(w, in) + return err +} + +func mustStat(path string) os.FileInfo { + fi, err := os.Stat(path) + if err != nil { + panic(err) + } + return fi +} + +func copyFile(src, dst string, mode os.FileMode) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + return err + } + defer out.Close() + _, err = io.Copy(out, in) + return err +} + +func dirSize(root string) (int64, error) { + var n int64 + err := filepath.Walk(root, func(_ string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + n += info.Size() + } + return nil + }) + return n, err +} + +func fileSize(path string) (int64, error) { + fi, err := os.Stat(path) + if err != nil { + return 0, err + } + return fi.Size(), nil +} + +func sanitizeVol(s string) string { + s = strings.ToUpper(s) + s = strings.Map(func(r rune) rune { + if (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '-' || r == '_' { + return r + } + return '-' + }, s) + if len(s) > 32 { + s = s[:32] + } + if s == "" { + s = "NAVIS" + } + return s +} + +func readmeText(version, arch string) string { + return fmt.Sprintf(`Navis %s for macOS (%s) + +1) Open this disk image +2) Drag Navis.app to Applications (or any folder) +3) First launch may need: + xattr -dr com.apple.quarantine /Applications/Navis.app +4) Run from Terminal: + /Applications/Navis.app/Contents/MacOS/Navis init + /Applications/Navis.app/Contents/MacOS/Navis install-core + /Applications/Navis.app/Contents/MacOS/Navis connect + +Or use the CLI binary next to this DMG on git (file named Navis). + +Shop: https://evilfox.win/ +`, version, arch) +} diff --git a/versioninfo.json b/versioninfo.json index e579216..cb3bb27 100644 --- a/versioninfo.json +++ b/versioninfo.json @@ -1,7 +1,7 @@ { "FixedFileInfo": { - "FileVersion": { "Major": 1, "Minor": 4, "Patch": 0, "Build": 0 }, - "ProductVersion": { "Major": 1, "Minor": 4, "Patch": 0, "Build": 0 }, + "FileVersion": { "Major": 1, "Minor": 4, "Patch": 1, "Build": 0 }, + "ProductVersion": { "Major": 1, "Minor": 4, "Patch": 1, "Build": 0 }, "FileFlagsMask": "3f", "FileFlags": "00", "FileOS": "40004", @@ -11,11 +11,11 @@ "StringFileInfo": { "CompanyName": "Navis", "FileDescription": "Navis — NaiveProxy / Hysteria 2 client for Windows and macOS", - "FileVersion": "1.4.0.0", + "FileVersion": "1.4.1.0", "InternalName": "Navis", "OriginalFilename": "Navis.exe", "ProductName": "Navis", - "ProductVersion": "1.4.0.0" + "ProductVersion": "1.4.1.0" }, "VarFileInfo": { "Translation": {