mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-03 17:58:42 +02:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
141a5f06a4 | ||
|
|
7a3857c06a | ||
|
|
ed26786fdb | ||
|
|
966268ff05 | ||
|
|
87ae696d7a | ||
|
|
7e92b290b6 |
@@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
|
> As of right now, the app is not signed by Apple. You need to have Gatekeeper disabled to run it. The app automatically checks for updates on each launch.
|
||||||
|
|
||||||
The app can be downloaded from the [releases page](https://github.com/zhom/donutbrowser/releases/latest).
|
The app can be downloaded from the [releases page](https://github.com/zhom/donutbrowser/releases/latest).
|
||||||
|
|
||||||
## Supported Platforms
|
## Supported Platforms
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "donutbrowser",
|
"name": "donutbrowser",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.1",
|
"version": "0.2.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
|
|||||||
Generated
+1
-1
@@ -973,7 +973,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "donutbrowser"
|
name = "donutbrowser"
|
||||||
version = "0.2.1"
|
version = "0.2.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "donutbrowser"
|
name = "donutbrowser"
|
||||||
version = "0.2.1"
|
version = "0.2.3"
|
||||||
description = "A Tauri App"
|
description = "Browser Orchestrator"
|
||||||
authors = ["zhom@github"]
|
authors = ["zhom@github"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.2.1</string>
|
<string>0.2.3</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use tauri::Emitter;
|
use tauri::Emitter;
|
||||||
|
|
||||||
|
use crate::extraction::Extractor;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct AppReleaseAsset {
|
pub struct AppReleaseAsset {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -370,73 +372,24 @@ impl AppAutoUpdater {
|
|||||||
Ok(file_path)
|
Ok(file_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the update (DMG on macOS)
|
/// Extract the update using the extraction module
|
||||||
async fn extract_update(
|
async fn extract_update(
|
||||||
&self,
|
&self,
|
||||||
dmg_path: &Path,
|
archive_path: &Path,
|
||||||
dest_dir: &Path,
|
dest_dir: &Path,
|
||||||
) -> Result<PathBuf, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<PathBuf, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
// For DMG files on macOS, we need to mount and copy the .app
|
let extractor = Extractor::new();
|
||||||
let mount_point = dest_dir.join("mount");
|
|
||||||
fs::create_dir_all(&mount_point)?;
|
|
||||||
|
|
||||||
// Mount the DMG
|
let extension = archive_path
|
||||||
let output = Command::new("hdiutil")
|
.extension()
|
||||||
.args([
|
.and_then(|ext| ext.to_str())
|
||||||
"attach",
|
.unwrap_or("");
|
||||||
"-nobrowse",
|
|
||||||
"-mountpoint",
|
|
||||||
mount_point.to_str().unwrap(),
|
|
||||||
dmg_path.to_str().unwrap(),
|
|
||||||
])
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
match extension {
|
||||||
return Err(
|
"dmg" => extractor.extract_dmg(archive_path, dest_dir).await,
|
||||||
format!(
|
"zip" => extractor.extract_zip(archive_path, dest_dir).await,
|
||||||
"Failed to mount DMG: {}",
|
_ => Err(format!("Unsupported archive format: {extension}").into()),
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the .app in the mount point
|
|
||||||
let app_entry = fs::read_dir(&mount_point)?
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.find(|entry| entry.path().extension().is_some_and(|ext| ext == "app"))
|
|
||||||
.ok_or("No .app found in DMG")?;
|
|
||||||
|
|
||||||
let app_path = dest_dir.join("extracted_app");
|
|
||||||
if app_path.exists() {
|
|
||||||
fs::remove_dir_all(&app_path)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the .app to extraction directory
|
|
||||||
let output = Command::new("cp")
|
|
||||||
.args([
|
|
||||||
"-R",
|
|
||||||
app_entry.path().to_str().unwrap(),
|
|
||||||
app_path.to_str().unwrap(),
|
|
||||||
])
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
if !output.status.success() {
|
|
||||||
return Err(
|
|
||||||
format!(
|
|
||||||
"Failed to copy app: {}",
|
|
||||||
String::from_utf8_lossy(&output.stderr)
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount the DMG
|
|
||||||
let _ = Command::new("hdiutil")
|
|
||||||
.args(["detach", mount_point.to_str().unwrap()])
|
|
||||||
.output();
|
|
||||||
|
|
||||||
Ok(app_path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Install the update by replacing the current app
|
/// Install the update by replacing the current app
|
||||||
@@ -701,4 +654,28 @@ mod tests {
|
|||||||
let url = url.unwrap();
|
let url = url.unwrap();
|
||||||
assert!(url.contains(".dmg"));
|
assert!(url.contains(".dmg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_update_uses_extractor() {
|
||||||
|
// This test verifies that the extract_update method properly uses the Extractor
|
||||||
|
// We can't run the actual extraction in unit tests without real DMG files,
|
||||||
|
// but we can verify the method signature and basic logic
|
||||||
|
let updater = AppAutoUpdater::new();
|
||||||
|
|
||||||
|
// Test that unsupported formats would be rejected
|
||||||
|
let temp_dir = std::env::temp_dir();
|
||||||
|
let unsupported_file = temp_dir.join("test.rar");
|
||||||
|
|
||||||
|
// Create a mock runtime to test the logic
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
// This would fail because .rar is not supported, which proves
|
||||||
|
// our method is using the Extractor logic
|
||||||
|
let result = rt.block_on(async { updater.extract_update(&unsupported_file, &temp_dir).await });
|
||||||
|
|
||||||
|
// Should fail with unsupported format error
|
||||||
|
assert!(result.is_err());
|
||||||
|
let error_msg = result.unwrap_err().to_string();
|
||||||
|
assert!(error_msg.contains("Unsupported archive format: rar"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ impl Extractor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn extract_dmg(
|
pub async fn extract_dmg(
|
||||||
&self,
|
&self,
|
||||||
dmg_path: &Path,
|
dmg_path: &Path,
|
||||||
dest_dir: &Path,
|
dest_dir: &Path,
|
||||||
@@ -149,7 +149,7 @@ impl Extractor {
|
|||||||
Ok(app_path)
|
Ok(app_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn extract_zip(
|
pub async fn extract_zip(
|
||||||
&self,
|
&self,
|
||||||
zip_path: &Path,
|
zip_path: &Path,
|
||||||
dest_dir: &Path,
|
dest_dir: &Path,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Donut Browser",
|
"productName": "Donut Browser",
|
||||||
"version": "0.2.1",
|
"version": "0.2.3",
|
||||||
"identifier": "com.donutbrowser",
|
"identifier": "com.donutbrowser",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
|||||||
Reference in New Issue
Block a user