Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 56 additions & 55 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,55 +1,56 @@
[package]
name = "spruceos-installer"
version = "1.5.0"
edition = "2021"
description = "SpruceOS SD Card Installer"
authors = ["SpruceOS Team (https://github.com/spruceUI)", "CMTag (https://github.com/CMTag)", "NextUI Team (https://github.com/LoveRetro)", "Helaas (https://github.com/Helaas)"]
license = "CC-BY-NC-4.0"

[features]
default = []
icon = []

[dependencies]
eframe = "0.33"
egui = "0.33"
egui_extras = { version = "0.33", features = ["all_loaders"] }
egui-thematic = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "fs", "process", "sync", "time", "io-util", "macros"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] }
sevenz-rust = "0.6"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
futures-util = "0.3"
tokio-util = "0.7"
dirs = "5"
tempfile = "3"
image = { version = "0.25", default-features = false, features = ["png", "qoi"] }
lazy_static = "1.4"
libc = "0.2"
sha2 = "0.10"
flate2 = "1.0"
arboard = "3.4"
regex = "1"
which = "6.0"

[target.'cfg(windows)'.dependencies]
windows = { version = "0.58", features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_IO",
"Win32_System_Ioctl",
"Win32_System_Registry",
"Win32_Security",
] }

[target.'cfg(windows)'.build-dependencies]
embed-resource = "2"

[profile.release]
opt-level = "z"
lto = true
strip = "debuginfo"



[package]
name = "spruceos-installer"
version = "1.6.0"
edition = "2021"
description = "SpruceOS SD Card Installer"
authors = ["SpruceOS Team (https://github.com/spruceUI)", "CMTag (https://github.com/CMTag)", "NextUI Team (https://github.com/LoveRetro)", "Helaas (https://github.com/Helaas)"]
license = "CC-BY-NC-4.0"

[features]
default = []
icon = []

[dependencies]
eframe = "0.33"
egui = "0.33"
egui_extras = { version = "0.33", features = ["all_loaders"] }
egui-thematic = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "fs", "process", "sync", "time", "io-util", "macros"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] }
sevenz-rust = "0.6"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
futures-util = "0.3"
tokio-util = "0.7"
dirs = "5"
tempfile = "3"
image = { version = "0.25", default-features = false, features = ["png", "qoi"] }
lazy_static = "1.4"
libc = "0.2"
sha2 = "0.10"
flate2 = "1.0"
zip = { version = "2", default-features = false, features = ["deflate"] }
arboard = "3.4"
regex = "1"
which = "6.0"

[target.'cfg(windows)'.dependencies]
windows = { version = "0.58", features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_IO",
"Win32_System_Ioctl",
"Win32_System_Registry",
"Win32_Security",
] }

[target.'cfg(windows)'.build-dependencies]
embed-resource = "2"

[profile.release]
opt-level = "z"
lto = true
strip = "debuginfo"



4 changes: 2 additions & 2 deletions assets/Mac/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<key>CFBundleIdentifier</key>
<string>com.spruceos.installer</string>
<key>CFBundleVersion</key>
<string>1.5.0</string>
<string>1.6.0</string>
<key>CFBundleShortVersionString</key>
<string>1.5.0</string>
<string>1.6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleExecutable</key>
Expand Down
22 changes: 17 additions & 5 deletions src/app/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,23 @@ impl InstallerApp {
.collect()
}

/// Whether an asset is a raw disk image to burn, rather than an archive
/// whose contents get copied onto a formatted card.
///
/// `.img.zip` is a raw image inside a zip container (BaseOS ships these) and
/// must be burned, so it is checked before the plain `.zip` archive case.
pub(super) fn is_raw_image_asset(name: &str) -> bool {
name.ends_with(".img.gz") ||
name.ends_with(".img.zip") ||
name.ends_with(".img")
}

/// Strip all known extensions from an asset name to get the base name
fn strip_extensions(name: &str) -> String {
let mut base = name.to_string();

// Remove known extensions in order of specificity
for ext in &[".img.gz", ".tar.gz", ".7z", ".zip", ".img"] {
for ext in &[".img.gz", ".img.zip", ".tar.gz", ".7z", ".zip", ".img"] {
if base.ends_with(ext) {
base = base.strip_suffix(ext).unwrap_or(&base).to_string();
break; // Only strip one extension
Expand All @@ -136,8 +147,10 @@ impl InstallerApp {

if base_names.len() == 1 {
// Same base name, different extensions - pick by priority
// Priority: .7z > .zip > .img.gz > .img
const PRIORITY: &[&str] = &[".7z", ".zip", ".img.gz", ".img"];
// Priority: .7z > .img.zip > .zip > .img.gz > .img
// .img.zip is listed before .zip so the plain-archive entry does not
// shadow it — every .img.zip name also ends with .zip.
const PRIORITY: &[&str] = &[".7z", ".img.zip", ".zip", ".img.gz", ".img"];

for ext in PRIORITY {
if let Some((idx, _)) = assets.iter()
Expand Down Expand Up @@ -399,8 +412,7 @@ impl InstallerApp {
log(&format!("Disk space check passed: {} MB available", available_space / 1_048_576));

// Detect installation mode: raw image vs archive
let is_raw_image = asset.name.ends_with(".img.gz") ||
asset.name.ends_with(".img");
let is_raw_image = Self::is_raw_image_asset(&asset.name);

if is_raw_image {
crate::debug::log("Detected RAW IMAGE mode - will burn image to device");
Expand Down
20 changes: 6 additions & 14 deletions src/app/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,9 @@ impl eframe::App for InstallerApp {
}

// Check if selected asset is a raw image
let is_raw_image = if let Some(idx) = auto_idx {
let asset_name = &self.available_assets[idx].name;
asset_name.ends_with(".img.gz") ||
asset_name.ends_with(".img")
} else {
false
};
let is_raw_image = auto_idx.is_some_and(|idx| {
Self::is_raw_image_asset(&self.available_assets[idx].name)
});

// If update mode and NOT a raw image, show preview modal; otherwise go to confirmation
if self.update_mode && !is_raw_image {
Expand Down Expand Up @@ -472,13 +468,9 @@ impl eframe::App for InstallerApp {
ui.add_enabled_ui(can_continue, |ui| {
if ui.button("Continue").clicked() {
// Check if selected asset is a raw image
let is_raw_image = if let Some(idx) = self.selected_asset_idx {
let asset_name = &self.available_assets[idx].name;
asset_name.ends_with(".img.gz") ||
asset_name.ends_with(".img")
} else {
false
};
let is_raw_image = self.selected_asset_idx.is_some_and(|idx| {
Self::is_raw_image_asset(&self.available_assets[idx].name)
});

// If update mode and NOT a raw image, show preview; otherwise go to confirmation
if self.update_mode && !is_raw_image {
Expand Down
Loading
Loading