//go:build darwin package update import ( "context" "fmt" "os" "os/exec" "path/filepath" "runtime" ) // Apply downloads the new binary into ~/Downloads (no silent self-replace). func Apply(ctx context.Context, manifestURL string) (string, error) { st, err := Check(ctx, manifestURL) if err != nil { return "", err } if !st.Available { if st.Error != "" { return "", fmt.Errorf("%s", st.Error) } return "", fmt.Errorf("обновление не найдено") } if !allowedDownloadURL(st.URL) { return "", fmt.Errorf("URL обновления должен быть на git.evilfox.cc или files.de4ima.uk") } home, err := os.UserHomeDir() if err != nil { return "", err } dir := filepath.Join(home, "Downloads") _ = os.MkdirAll(dir, 0o755) dest := filepath.Join(dir, fmt.Sprintf("Navis-%s-darwin-%s", sanitizeDarwinVer(st.Latest), runtime.GOARCH)) tmp := dest + ".part" _ = os.Remove(tmp) _ = os.Remove(dest) if err := downloadFile(ctx, st.URL, tmp); err != nil { return "", err } if st.SHA256 != "" { sum, err := fileSHA256(tmp) if err != nil { _ = os.Remove(tmp) return "", err } if sum != "" && !eqFold(sum, st.SHA256) { _ = os.Remove(tmp) return "", fmt.Errorf("checksum mismatch") } } if err := os.Rename(tmp, dest); err != nil { _ = os.Remove(tmp) return "", err } _ = os.Chmod(dest, 0o755) _ = exec.Command("open", "-R", dest).Start() return fmt.Sprintf("Скачано v%s:\n%s\nЗакройте Navis и запустите новый файл.", st.Latest, dest), nil } func sanitizeDarwinVer(v string) string { out := make([]rune, 0, len(v)) for _, r := range v { switch { case r >= '0' && r <= '9', r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r == '.', r == '-': out = append(out, r) } } if len(out) == 0 { return "update" } return string(out) } func eqFold(a, b string) bool { if len(a) != len(b) { return false } for i := 0; i < len(a); i++ { ca, cb := a[i], b[i] if ca >= 'A' && ca <= 'F' { ca += 'a' - 'A' } if cb >= 'A' && cb <= 'F' { cb += 'a' - 'A' } if ca != cb { return false } } return true }