Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
df5b174
chore(ci): ignore RUSTSEC-2026-0173 proc-macro-error2 advisory
risu729 Jun 8, 2026
bce0b74
Merge branch 'main' into chore/ignore-proc-macro-error2-advisory
risu729 Jun 8, 2026
dd21792
refactor(file): split unarchive from untar
risu729 Jun 5, 2026
dafdf39
test(file): cover unarchive zip dispatch
risu729 Jun 5, 2026
e0c33c4
refactor(file): add tbz alias to TarFormat
risu729 Jun 6, 2026
b99f327
refactor(file): rename un_compressed_file to decompress_file
risu729 Jun 6, 2026
63acd6c
refactor(aqua-registry): stop canonicalizing format aliases
risu729 Jun 6, 2026
897bb32
refactor(java): detect archive format from filename only
risu729 Jun 6, 2026
740cdde
refactor(file): remove unarchive_with_format wrapper
risu729 Jun 6, 2026
4f47e4b
refactor: inline archive opts and trim aqua format check
risu729 Jun 6, 2026
4c5e386
refactor(file): rename TarFormat and split extract from decompress
risu729 Jun 7, 2026
c27b66c
fix(aqua): fail clearly on unimplemented extraction formats
risu729 Jun 7, 2026
cc685ca
refactor(file): add unsupported aqua formats to ArchiveFormat
risu729 Jun 8, 2026
79912e9
fix(java): prefer metadata file_type for archive extraction
risu729 Jun 8, 2026
d9d5b2c
refactor(aqua): deduplicate make_executable in archive install
risu729 Jun 8, 2026
cf70709
fix(file): return errors for unsupported archive formats
risu729 Jun 8, 2026
cbf921e
fix(http): remove needless ExtractOptions struct update
risu729 Jun 8, 2026
78f4469
refactor(file): derive is_archive from is_tar_archive
risu729 Jun 8, 2026
4e3faf7
refactor(file): derive ArchiveFormat extension from strum Display
risu729 Jun 8, 2026
f4d83dd
refactor(file): remove TarOptions in favor of ExtractOptions
risu729 Jun 8, 2026
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
14 changes: 2 additions & 12 deletions crates/aqua-registry/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,12 +470,7 @@ impl AquaPackage {

for format in formats {
if asset_name.ends_with(&format!(".{format}")) {
return match format {
"tgz" => "tar.gz",
"txz" => "tar.xz",
"tbz2" | "tbz" => "tar.bz2",
_ => format,
};
return format;
}
}
"raw"
Expand Down Expand Up @@ -570,12 +565,7 @@ impl AquaPackage {
};
self.detect_format(&asset)
} else {
match self.format.as_str() {
"tgz" => "tar.gz",
"txz" => "tar.xz",
"tbz2" | "tbz" => "tar.bz2",
format => format,
}
self.format.as_str()
};
Ok(format)
}
Expand Down
1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ feature-depth = 1
# output a note when they are encountered.
ignore = [
{ id = "RUSTSEC-2024-0370", reason = "proc-macro-error dependency from sigstore crate - no safe upgrade available" },
{ id = "RUSTSEC-2026-0173", reason = "proc-macro-error2 unmaintained - transitive via age/mlua/tabled/rops, no safe upgrade available" },
{ id = "RUSTSEC-2023-0071", reason = "rsa crate Marvin attack vulnerability from sigstore crate - no safe upgrade available" },
{ id = "RUSTSEC-2025-0119", reason = "number_prefix crate is unmaintained - used by indicatif/self_update, no safe upgrade available" },
# rustls-webpki 0.102.8 advisories — pulled in transitively by sigstore-tsa
Expand Down
42 changes: 15 additions & 27 deletions src/backend/aqua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::backend::static_helpers::get_filename_from_url;
use crate::cli::args::BackendArg;
use crate::cli::version::{ARCH, OS};
use crate::config::Settings;
use crate::file::{TarFormat, TarOptions};
use crate::file::{ArchiveFormat, ExtractOptions};
use crate::http::HTTP;
use crate::install_context::InstallContext;
use crate::lockfile::{PlatformInfo, ProvenanceType};
Expand Down Expand Up @@ -1272,7 +1272,8 @@ impl AquaBackend {
v: &str,
) -> Result<bool> {
let format = pkg.format(v, os(), arch())?;
let format = TarFormat::from_ext(format);
let format = ArchiveFormat::from_ext(format)
.ok_or_else(|| eyre!("unsupported archive format for SLSA content check"))?;
if !format.is_archive() {
return Err(eyre!(
"SLSA provenance subject mismatch and content-level fallback is only supported for archives"
Expand Down Expand Up @@ -2282,13 +2283,15 @@ impl AquaBackend {
let first_bin_path = bin_paths
.first()
.expect("at least one bin path should exist");
let tar_opts = TarOptions {
let extract_opts = ExtractOptions {
pr: Some(ctx.pr.as_ref()),
..TarOptions::new(TarFormat::from_ext(format))
..Default::default()
};
let archive_format = ArchiveFormat::from_ext(format);
let mut make_executable = false;
if let AquaPackageType::GithubArchive = pkg.r#type {
file::untar(&tarball_path, &install_path, &tar_opts)?;
let archive_format = archive_format.unwrap_or(ArchiveFormat::TarGz);
file::extract_archive(&tarball_path, &install_path, archive_format, &extract_opts)?;
} else if let AquaPackageType::GithubContent = pkg.r#type {
file::create_dir_all(&install_path)?;
file::copy(&tarball_path, first_bin_path)?;
Expand All @@ -2297,32 +2300,17 @@ impl AquaBackend {
file::create_dir_all(&install_path)?;
file::copy(&tarball_path, first_bin_path)?;
make_executable = true;
} else if format.starts_with("tar") || (format == "7z" && cfg!(windows)) {
file::untar(&tarball_path, &install_path, &tar_opts)?;
make_executable = true;
} else if format == "zip" {
file::unzip(&tarball_path, &install_path, &Default::default())?;
make_executable = true;
} else if format == "gz" {
file::create_dir_all(&install_path)?;
file::un_gz(&tarball_path, first_bin_path)?;
make_executable = true;
} else if format == "xz" {
file::create_dir_all(&install_path)?;
file::un_xz(&tarball_path, first_bin_path)?;
make_executable = true;
} else if format == "zst" {
file::create_dir_all(&install_path)?;
file::un_zst(&tarball_path, first_bin_path)?;
make_executable = true;
} else if format == "bz2" {
file::create_dir_all(&install_path)?;
file::un_bz2(&tarball_path, first_bin_path)?;
make_executable = true;
} else if format == "dmg" {
file::un_dmg(&tarball_path, &install_path)?;
} else if format == "pkg" {
file::un_pkg(&tarball_path, &install_path)?;
} else if let Some(archive_format) = archive_format {
if archive_format.is_compressed_file() {
file::decompress_file(&tarball_path, first_bin_path, archive_format)?;
} else {
file::extract_archive(&tarball_path, &install_path, archive_format, &extract_opts)?;
}
make_executable = true;
} else {
bail!("unsupported format: {}", format);
}
Expand Down
6 changes: 3 additions & 3 deletions src/backend/asset_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::sync::LazyLock;
use super::platform_target::PlatformTarget;
use super::platform_tokens::is_platform_or_version_token;
use super::static_helpers::get_filename_from_url;
use crate::file::TarFormat;
use crate::file::ArchiveFormat;
use crate::http::HTTP;

// ========== Platform Detection Types (from asset_detector) ==========
Expand Down Expand Up @@ -352,9 +352,9 @@ impl AssetPicker {
}

fn score_format_preferences(&self, asset: &str) -> i32 {
let format = TarFormat::from_file_name(asset);
let format = ArchiveFormat::from_file_name(asset);

if format == TarFormat::Zip {
if format == ArchiveFormat::Zip {
if self.target_os == "windows" {
return 15;
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/backend/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1880,9 +1880,9 @@ impl UnifiedGitBackend {
) -> Result<bool> {
let raw_opts = tv.request.options();
let format = if let Some(format_opt) = lookup_with_fallback(&raw_opts, "format") {
file::TarFormat::from_ext(&format_opt)
file::ArchiveFormat::from_ext(&format_opt).unwrap_or(file::ArchiveFormat::Raw)
} else {
file::TarFormat::from_file_name(
file::ArchiveFormat::from_file_name(
&file_path.file_name().unwrap_or_default().to_string_lossy(),
)
};
Expand Down
41 changes: 15 additions & 26 deletions src/backend/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct FileInfo {
/// File extension
extension: String,
/// Detected archive format
format: file::TarFormat,
format: file::ArchiveFormat,
/// Whether this is a compressed single binary (not a tar archive)
is_compressed_binary: bool,
}
Expand All @@ -81,20 +81,17 @@ impl FileInfo {
};

let file_name = effective_path.file_name().unwrap().to_string_lossy();
let format = file::TarFormat::from_file_name(&file_name);
let format = file::ArchiveFormat::from_file_name(&file_name);

let extension = format
.extension()
.map(|s| s.to_string())
.unwrap_or_else(|| {
effective_path
.extension()
.and_then(|s| s.to_str())
.unwrap_or("")
.to_string()
});
let extension = format.extension().unwrap_or_else(|| {
effective_path
.extension()
.and_then(|s| s.to_str())
.unwrap_or("")
.to_string()
});

let is_compressed_binary = !format.is_archive() && format != file::TarFormat::Raw;
let is_compressed_binary = !format.is_archive() && format != file::ArchiveFormat::Raw;

Self {
effective_path,
Expand Down Expand Up @@ -286,7 +283,7 @@ impl HttpBackend {
/// used different options (e.g., different `bin` name)
fn extraction_type_from_cache(&self, cache_key: &str, file_info: &FileInfo) -> ExtractionType {
// For archives, we don't need to detect the filename
if !file_info.is_compressed_binary && file_info.format != file::TarFormat::Raw {
if !file_info.is_compressed_binary && file_info.format != file::ArchiveFormat::Raw {
return ExtractionType::Archive;
}

Expand Down Expand Up @@ -369,7 +366,7 @@ impl HttpBackend {

if file_info.is_compressed_binary {
self.extract_compressed_binary(dest, file_path, &file_info, opts, pr)
} else if file_info.format == file::TarFormat::Raw {
} else if file_info.format == file::ArchiveFormat::Raw {
self.extract_raw_file(dest, file_path, &file_info, opts, pr)
} else {
self.extract_archive(tv, dest, file_path, &file_info, opts, pr)
Expand All @@ -393,14 +390,7 @@ impl HttpBackend {
pr.set_message(format!("extract {}", file_info.file_name()));
}

file::untar(
file_path,
&dest_file,
&file::TarOptions {
pr,
..file::TarOptions::new(file_info.format)
},
)?;
file::decompress_file(file_path, &dest_file, file_info.format)?;

file::make_executable(&dest_file)?;
Ok(ExtractionType::RawFile { filename })
Expand Down Expand Up @@ -451,14 +441,13 @@ impl HttpBackend {
strip_components = Some(1);
}

let tar_opts = file::TarOptions {
format: file_info.format,
let extract_opts = file::ExtractOptions {
strip_components: strip_components.unwrap_or(0),
pr,
preserve_mtime: false,
};

file::untar(file_path, dest, &tar_opts)?;
file::extract_archive(file_path, dest, file_info.format, &extract_opts)?;

// Handle rename_exe option for archives
if let Some(rename_to) = opts.rename_exe() {
Expand Down
6 changes: 1 addition & 5 deletions src/backend/spm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,11 +483,7 @@ impl SPMBackend {
self.verify_checksum(ctx, tv, &download_path)?;

ctx.pr.set_message(format!("extract {}", asset.name));
file::untar(
&download_path,
&bundle_dir,
&file::TarOptions::new(file::TarFormat::Zip),
)?;
file::unzip(&download_path, &bundle_dir, &Default::default())?;

let triples = swift_target_triples(ctx, self, tv).await?;
let binaries = artifactbundle_binaries(&bundle_dir, &triples)?;
Expand Down
25 changes: 9 additions & 16 deletions src/backend/static_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,20 +399,20 @@ pub fn install_artifact(
file::remove_all(&install_path)?;
file::create_dir_all(&install_path)?;

// Use TarFormat for format detection
// Use ArchiveFormat for format detection
// Check for explicit format option first, then fall back to file extension
let format = if let Some(format_opt) = lookup_with_fallback(opts, "format") {
file::TarFormat::from_ext(&format_opt)
file::ArchiveFormat::from_ext(&format_opt).unwrap_or(file::ArchiveFormat::Raw)
} else {
file::TarFormat::from_file_name(
file::ArchiveFormat::from_file_name(
&file_path.file_name().unwrap_or_default().to_string_lossy(),
)
};

// Get file extension and detect format
let file_name = file_path.file_name().unwrap().to_string_lossy();

if !format.is_archive() && format != file::TarFormat::Raw {
if !format.is_archive() && format != file::ArchiveFormat::Raw {
// Handle compressed single binary
let ext = Path::new(&*file_name)
.extension()
Expand All @@ -435,17 +435,10 @@ pub fn install_artifact(
install_path.join(cleaned_name)
};

file::untar(
file_path,
&dest,
&file::TarOptions {
pr,
..file::TarOptions::new(format)
},
)?;
file::decompress_file(file_path, &dest, format)?;

file::make_executable(&dest)?;
} else if format == file::TarFormat::Raw {
} else if format == file::ArchiveFormat::Raw {
// Copy the file directly to the bin_path directory or install_path
if let Some(bin_path_template) = lookup_with_fallback(opts, "bin_path") {
let bin_path = template_string(&bin_path_template, tv);
Expand Down Expand Up @@ -479,14 +472,14 @@ pub fn install_artifact(
debug!("Auto-detected single directory archive, extracting with strip_components=1");
strip_components = Some(1);
}
let tar_opts = file::TarOptions {
let extract_opts = file::ExtractOptions {
strip_components: strip_components.unwrap_or(0),
pr,
..file::TarOptions::new(format)
..Default::default()
};

// Extract with determined strip_components
file::untar(file_path, &install_path, &tar_opts)?;
file::extract_archive(file_path, &install_path, format, &extract_opts)?;

// Extract just the repo name from tool_name (e.g., "opsgenie/opsgenie-lamp" -> "opsgenie-lamp")
let full_tool_name = tv.ba().tool_name.as_str();
Expand Down
31 changes: 18 additions & 13 deletions src/cli/generate/tool_stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::backend::platform_target::PlatformTarget;
use crate::backend::static_helpers::get_filename_from_url;
use crate::cli::tool_stub::ToolStubFile;
use crate::config::Config;
use crate::file::{self, TarFormat, TarOptions};
use crate::file::{self, ArchiveFormat};
use crate::http::HTTP;
use crate::lockfile::PlatformInfo;
use crate::minisign;
Expand Down Expand Up @@ -471,7 +471,7 @@ exec "$MISE_BIN" tool-stub "$0" "$@"
let checksum = format!("blake3:{}", blake3::hash(&bytes).to_hex());

// Detect binary path if this is an archive
let bin_path = if TarFormat::from_file_name(&filename).is_archive() {
let bin_path = if ArchiveFormat::from_file_name(&filename).is_archive() {
// Update progress message for extraction and reuse the same progress reporter
pr.set_message(format!("extract {filename}"));
match self
Expand Down Expand Up @@ -508,20 +508,25 @@ exec "$MISE_BIN" tool-stub "$0" "$@"
std::fs::create_dir_all(&extracted_dir)?;

// Try extraction using mise's built-in extraction logic (reuse the passed progress reporter)
let tar_opts = TarOptions {
pr: Some(pr),
..TarOptions::new(TarFormat::from_file_name(
&archive_path
.file_name()
.unwrap_or_default()
.to_string_lossy(),
))
};
file::untar(archive_path, &extracted_dir, &tar_opts)?;
let format = ArchiveFormat::from_file_name(
&archive_path
.file_name()
.unwrap_or_default()
.to_string_lossy(),
);
file::extract_archive(
archive_path,
&extracted_dir,
format,
&file::ExtractOptions {
pr: Some(pr),
..Default::default()
},
)?;

// Check if strip_components would be applied during actual installation
let format =
TarFormat::from_file_name(&archive_path.file_name().unwrap().to_string_lossy());
ArchiveFormat::from_file_name(&archive_path.file_name().unwrap().to_string_lossy());
let will_strip = file::should_strip_components(archive_path, format)?;

// Find executable files
Expand Down
Loading
Loading