Compare commits

...

9 Commits

Author SHA1 Message Date
zhom 7a3857c06a chore: version bump test 2025-05-31 12:53:50 +04:00
zhom ed26786fdb chore: cargo fmt 2025-05-31 12:38:14 +04:00
zhom 966268ff05 chore: version bump 2025-05-31 12:37:29 +04:00
zhom 87ae696d7a refactor: make app_auto_updater use shared extraction logic 2025-05-31 12:31:16 +04:00
zhom 7e92b290b6 chore: update description and readme 2025-05-31 12:14:54 +04:00
zhom eb62e0abf9 chor: bump version 2025-05-31 11:39:23 +04:00
zhom 0ed5adf2ba chore: cargo fmt 2025-05-31 11:09:42 +04:00
zhom dd91aaeea0 chore: don't make regular release a draft 2025-05-31 11:08:50 +04:00
zhom 6a3407796d chore: version bump and build update 2025-05-31 11:07:59 +04:00
11 changed files with 71 additions and 73 deletions
+1 -1
View File
@@ -131,6 +131,6 @@ jobs:
tagName: ${{ github.ref_name }}
releaseName: "Donut Browser ${{ github.ref_name }}"
releaseBody: "See the assets to download this version and install."
releaseDraft: true
releaseDraft: false
prerelease: false
args: ${{ matrix.args }}
+1
View File
@@ -94,6 +94,7 @@ jobs:
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_TAG: "nightly-${{ steps.commit.outputs.hash }}"
GITHUB_REF_NAME: "nightly-${{ steps.commit.outputs.hash }}"
GITHUB_SHA: ${{ github.sha }}
with:
+2
View File
@@ -27,6 +27,8 @@
## Download
> As of right now, the app is not signed by Apple. You need to have Gatekeeper disabled to run it.
The app can be downloaded from the [releases page](https://github.com/zhom/donutbrowser/releases/latest).
## Supported Platforms
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "donutbrowser",
"private": true,
"version": "0.1.0",
"version": "0.2.3",
"type": "module",
"scripts": {
"dev": "next dev --turbopack",
+1 -1
View File
@@ -973,7 +973,7 @@ dependencies = [
[[package]]
name = "donutbrowser"
version = "0.1.0"
version = "0.2.3"
dependencies = [
"async-trait",
"base64 0.22.1",
+2 -2
View File
@@ -1,7 +1,7 @@
[package]
name = "donutbrowser"
version = "0.1.0"
description = "A Tauri App"
version = "0.2.3"
description = "Browser Orchestrator"
authors = ["zhom@github"]
edition = "2021"
+1 -1
View File
@@ -13,7 +13,7 @@
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<string>0.2.3</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleIconFile</key>
+5 -2
View File
@@ -6,8 +6,11 @@ fn main() {
}
// Inject build version based on environment variables set by CI
if let Ok(tag_name) = std::env::var("GITHUB_REF_NAME") {
// This is set by GitHub Actions to the tag name (e.g., "v1.0.0" or "nightly-abc123")
if let Ok(build_tag) = std::env::var("BUILD_TAG") {
// Custom BUILD_TAG takes highest priority (used for nightly builds)
println!("cargo:rustc-env=BUILD_VERSION={build_tag}");
} else if let Ok(tag_name) = std::env::var("GITHUB_REF_NAME") {
// This is set by GitHub Actions to the tag name (e.g., "v1.0.0")
println!("cargo:rustc-env=BUILD_VERSION={tag_name}");
} else if std::env::var("STABLE_RELEASE").is_ok() {
// Fallback for stable releases - use CARGO_PKG_VERSION with 'v' prefix
+54 -62
View File
@@ -6,6 +6,8 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use tauri::Emitter;
use crate::extraction::Extractor;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AppReleaseAsset {
pub name: String,
@@ -47,8 +49,19 @@ impl AppAutoUpdater {
/// Check if running a nightly build based on environment variable
pub fn is_nightly_build() -> bool {
// If STABLE_RELEASE env var is set at compile time, it's a stable build
// Otherwise, it's a nightly build
option_env!("STABLE_RELEASE").is_none()
if option_env!("STABLE_RELEASE").is_some() {
return false;
}
// Also check if the current version starts with "nightly-"
let current_version = Self::get_current_version();
if current_version.starts_with("nightly-") {
return true;
}
// If STABLE_RELEASE is not set and version doesn't start with "nightly-",
// it's still considered a nightly build (dev builds, main branch builds, etc.)
true
}
/// Get current app version from build-time injection
@@ -359,73 +372,24 @@ impl AppAutoUpdater {
Ok(file_path)
}
/// Extract the update (DMG on macOS)
/// Extract the update using the extraction module
async fn extract_update(
&self,
dmg_path: &Path,
archive_path: &Path,
dest_dir: &Path,
) -> Result<PathBuf, Box<dyn std::error::Error + Send + Sync>> {
// For DMG files on macOS, we need to mount and copy the .app
let mount_point = dest_dir.join("mount");
fs::create_dir_all(&mount_point)?;
let extractor = Extractor::new();
// Mount the DMG
let output = Command::new("hdiutil")
.args([
"attach",
"-nobrowse",
"-mountpoint",
mount_point.to_str().unwrap(),
dmg_path.to_str().unwrap(),
])
.output()?;
let extension = archive_path
.extension()
.and_then(|ext| ext.to_str())
.unwrap_or("");
if !output.status.success() {
return Err(
format!(
"Failed to mount DMG: {}",
String::from_utf8_lossy(&output.stderr)
)
.into(),
);
match extension {
"dmg" => extractor.extract_dmg(archive_path, dest_dir).await,
"zip" => extractor.extract_zip(archive_path, dest_dir).await,
_ => Err(format!("Unsupported archive format: {extension}").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
@@ -594,6 +558,10 @@ mod tests {
// This will depend on whether STABLE_RELEASE is set during test compilation
let is_nightly = AppAutoUpdater::is_nightly_build();
println!("Is nightly build: {is_nightly}");
// The result should be true for test builds since STABLE_RELEASE is not set
// unless the test is run in a stable release environment
assert!(is_nightly || option_env!("STABLE_RELEASE").is_some());
}
#[test]
@@ -686,4 +654,28 @@ mod tests {
let url = url.unwrap();
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"));
}
}
+2 -2
View File
@@ -46,7 +46,7 @@ impl Extractor {
}
}
async fn extract_dmg(
pub async fn extract_dmg(
&self,
dmg_path: &Path,
dest_dir: &Path,
@@ -149,7 +149,7 @@ impl Extractor {
Ok(app_path)
}
async fn extract_zip(
pub async fn extract_zip(
&self,
zip_path: &Path,
dest_dir: &Path,
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Donut Browser",
"version": "0.1.0",
"version": "0.2.3",
"identifier": "com.donutbrowser",
"build": {
"beforeDevCommand": "pnpm dev",