Release 2.7.2+1: stop auto-apply update loop; check-only on start, apply on button with skip/opt-in.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Navis
2026-07-29 17:24:11 +03:00
co-authored by Cursor
parent 53eb845d04
commit 563edc4db8
29 changed files with 189 additions and 92 deletions
+7
View File
@@ -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`.
+3 -2
View File
@@ -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")
+3 -3
View File
@@ -34,11 +34,11 @@ if errorlevel 1 exit /b 1
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 2.7.2 -build 2.7.2.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 2.7.1 -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.1 -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 2.7.2 -build 2.7.2.1 -arch universal
if errorlevel 1 exit /b 1
echo Built Mac GUI + CLI:
+16 -11
View File
@@ -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 {
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+8 -8
View File
@@ -1,22 +1,22 @@
{
"version": "2.7.1",
"notes": "macOS: UDZO DMG, Application Support cores path, naiveproxy asset name fix",
"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": "8951c351b224d8a13e2772b6ee080d7bee3285ac162dd20c0855a8653a569ec5",
"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"
}
}
}
}
Binary file not shown.
+8 -8
View File
@@ -1,22 +1,22 @@
{
"version": "2.7.1",
"notes": "macOS: UDZO DMG, Application Support cores path, naiveproxy asset name fix",
"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": "8951c351b224d8a13e2772b6ee080d7bee3285ac162dd20c0855a8653a569ec5",
"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"
}
}
}
}
+14 -9
View File
@@ -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 {
+42 -5
View File
@@ -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 @@
<div class="update-banner" id="updateBanner">
<strong id="updateTitle">Доступно обновление</strong>
<p id="updateNotes"></p>
<button class="action primary" id="updateBtn" type="button">Обновить и перезапустить</button>
<div class="update-actions">
<button class="action primary" id="updateBtn" type="button">Обновить</button>
<button class="action secondary" id="skipUpdateBtn" type="button">Пропустить эту версию</button>
</div>
</div>
<header class="top">
@@ -725,7 +737,7 @@
<div class="brand-wrap">
<div class="brand-row">
<h1 class="brand">Navis</h1>
<span class="badge-ver">2.7</span>
<span class="badge-ver">2.7.1</span>
</div>
<p class="tagline">Быстрый клиент · Naive · Hy2 · AWG · Xray</p>
</div>
@@ -938,7 +950,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;
@@ -1173,10 +1196,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");
}
@@ -1279,7 +1309,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;
});
}
@@ -1444,6 +1474,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 });
+51 -27
View File
@@ -16,12 +16,27 @@ import (
"time"
)
// CurrentVersion is the shipped client version.
const CurrentVersion = "2.7.1"
// 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 = 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"
// 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"`
@@ -90,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 {
@@ -109,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 a<b, 0 if equal (semver-ish dotted ints).
// Compare returns 1 if a>b, -1 if a<b, 0 if equal.
//
// Update rule: only product semver (major.minor.patch) counts. Build metadata
// ("+N", "-suffix", or a 4th dotted component) is ignored so a just-updated app
// on the same product version is never treated as outdated vs the same feed.
func Compare(a, b string) int {
as := splitVer(a)
bs := splitVer(b)
n := len(as)
if len(bs) > 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 {
@@ -149,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)
}
@@ -172,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)
@@ -195,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 {
@@ -227,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 "<exe>.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 {
@@ -237,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")
@@ -250,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.
+13
View File
@@ -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) {
+1 -1
View File
@@ -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"
+8 -8
View File
@@ -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"
}
}
}
}
+11 -6
View File
@@ -20,19 +20,24 @@ 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")
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 <Navis> -out <dir> [-version 1.4.1] [-arch arm64]")
fmt.Fprintln(os.Stderr, "usage: packmac -bin <Navis> -out <dir> [-version 2.7.2] [-build 2.7.2.1] [-arch arm64]")
os.Exit(2)
}
if err := run(*bin, *outDir, *version, *arch); err != nil {
bundleVer := *build
if bundleVer == "" {
bundleVer = *version
}
if err := run(*bin, *outDir, *version, bundleVer, *arch); err != nil {
fmt.Fprintf(os.Stderr, "packmac: %v\n", err)
os.Exit(1)
}
}
func run(binPath, outDir, version, arch string) error {
func run(binPath, outDir, version, bundleVersion, arch string) error {
if err := os.MkdirAll(outDir, 0o755); err != nil {
return err
}
@@ -43,7 +48,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, bundleVersion, arch); err != nil {
return err
}
if err := signAppBundle(appRoot); err != nil {
@@ -123,7 +128,7 @@ func writeHdiutilDMG(dmgPath, stageDir, volume string) error {
return nil
}
func writeAppBundle(appRoot, binPath, version, arch string) error {
func writeAppBundle(appRoot, binPath, version, bundleVersion, arch string) error {
macOSDir := filepath.Join(appRoot, "Contents", "MacOS")
if err := os.MkdirAll(macOSDir, 0o755); err != nil {
return err
@@ -145,7 +150,7 @@ func writeAppBundle(appRoot, binPath, version, arch string) error {
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>%s
</dict>
</plist>
`, version, version, archPriority)
`, version, bundleVersion, archPriority)
if err := os.WriteFile(filepath.Join(appRoot, "Contents", "Info.plist"), []byte(plist), 0o644); err != nil {
return err
}
+4 -4
View File
@@ -1,7 +1,7 @@
{
"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": 1 },
"ProductVersion": { "Major": 2, "Minor": 7, "Patch": 2, "Build": 1 },
"FileFlagsMask": "3f",
"FileFlags": "00",
"FileOS": "40004",
@@ -11,12 +11,12 @@
"StringFileInfo": {
"CompanyName": "EvilFox",
"FileDescription": "Navis 2 — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)",
"FileVersion": "2.7.1.0",
"FileVersion": "2.7.2.1",
"InternalName": "Navis",
"LegalCopyright": "Copyright (c) EvilFox",
"OriginalFilename": "Navis.exe",
"ProductName": "Navis",
"ProductVersion": "2.7.1.0",
"ProductVersion": "2.7.2.1",
"Comments": "Open-source VPN/proxy client. https://evilfox.win/"
},
"VarFileInfo": {