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
17 changes: 17 additions & 0 deletions docs/dev-tools/backends/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,23 @@ bin = "docker-compose" # Rename from docker-compose-linux-x86_64 to docker-comp
When downloading single binaries (not archives), mise automatically removes OS/arch suffixes from the filename. For example, `docker-compose-linux-x86_64` becomes `docker-compose` automatically. Use the `bin` option only when you need a specific custom name.
:::

### `rename_exe`

Rename the executable inside an extracted archive to a specific name. This is useful when archives contain binaries with platform-specific names or when installing kubectl plugins that need specific naming:

```toml
[tools."http:openunison-cli"]
version = "1.0.0"
url = "https://nexus.tremolo.io/repository/openunison-cli/openunison-cli-v{{version}}-linux.zip"
rename_exe = "kubectl-openunison-cli" # Rename extracted binary for kubectl plugin
```

This works by searching for the first executable in the extracted directory (or `bin_path` if specified) and renaming it to the specified name.

::: tip
Use `bin` for renaming single binary downloads, and `rename_exe` for renaming executables inside archives.
:::

### `format`

Explicitly specify the archive format when the URL lacks a file extension or has an incorrect extension:
Expand Down
32 changes: 32 additions & 0 deletions e2e/backend/test_http_rename_exe
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# Test HTTP backend rename_exe option for renaming executables in archives

set -euo pipefail
export MISE_EXPERIMENTAL=1

# Test: rename_exe renames the executable inside the archive
# The hello-world archive contains hello-world-1.0.0/bin/hello-world
# We use strip_components=1 and bin_path=bin to get to the binary,
# then rename_exe to rename it to "my-hello"
cat <<EOF >mise.toml
[tools]
"http:hello-rename" = { version = "1.0.0", url = "https://mise.jdx.dev/test-fixtures/hello-world-1.0.0.tar.gz", strip_components = 1, bin_path = "bin", rename_exe = "my-hello", postinstall = "chmod +x \$MISE_TOOL_INSTALL_PATH/bin/my-hello" }
EOF

mise install
mise env

# Verify the renamed binary works
assert_contains "mise x -- my-hello" "hello world"

# Test: rename_exe with bin_path - binary should be renamed in the bin_path directory
cat <<EOF >mise.toml
[tools]
"http:fd-rename" = { version = "8.7.0", url = "https://mise.jdx.dev/test-fixtures/fd-8.7.0.tar.gz", bin_path = "fd-8.7.0/bin", rename_exe = "my-fd", postinstall = "chmod +x \$MISE_TOOL_INSTALL_PATH/fd-8.7.0/bin/my-fd" }
EOF

mise install
mise env

# Verify the renamed binary works
assert_contains "mise x -- my-fd --version" "8.7.0"
43 changes: 39 additions & 4 deletions src/backend/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::backend::VersionInfo;
use crate::backend::backend_type::BackendType;
use crate::backend::static_helpers::{
clean_binary_name, get_filename_from_url, list_available_platforms_with_key,
lookup_platform_key, template_string, verify_artifact,
lookup_platform_key, rename_executable_in_dir, template_string, verify_artifact,
Comment thread
cursor[bot] marked this conversation as resolved.
};
use crate::backend::version_list;
use crate::cli::args::BackendArg;
Expand Down Expand Up @@ -168,6 +168,16 @@ impl HttpBackend {
parts.push(format!("strip_{strip}"));
}

// Include rename_exe in cache key since it modifies the extracted content
if let Some(rename) = get_opt(opts, "rename_exe") {
parts.push(format!("rename_{rename}"));
// When rename_exe is used, bin_path affects where the rename happens,
// so different bin_path values result in different cached content
if let Some(bin_path) = get_opt(opts, "bin_path") {
parts.push(format!("binpath_{bin_path}"));
}
}
Comment thread
cursor[bot] marked this conversation as resolved.

let key = parts.join("_");
debug!("Cache key: {}", key);
Ok(key)
Expand Down Expand Up @@ -236,6 +246,7 @@ impl HttpBackend {
/// Extract artifact to cache with atomic rename
fn extract_to_cache(
&self,
tv: &ToolVersion,
file_path: &Path,
cache_key: &str,
url: &str,
Expand All @@ -261,7 +272,7 @@ impl HttpBackend {
}

// Perform extraction
let extraction_type = self.extract_artifact(&tmp_path, file_path, opts, pr)?;
let extraction_type = self.extract_artifact(tv, &tmp_path, file_path, opts, pr)?;

// Atomic replace
if cache_path.exists() {
Expand All @@ -278,6 +289,7 @@ impl HttpBackend {
/// Extract a single artifact to the given directory
fn extract_artifact(
&self,
tv: &ToolVersion,
dest: &Path,
file_path: &Path,
opts: &ToolVersionOptions,
Expand All @@ -292,7 +304,7 @@ impl HttpBackend {
} else if file_info.format == file::TarFormat::Raw {
self.extract_raw_file(dest, file_path, &file_info, opts, pr)
} else {
self.extract_archive(dest, file_path, &file_info, opts, pr)
self.extract_archive(tv, dest, file_path, &file_info, opts, pr)
}
}

Expand Down Expand Up @@ -371,6 +383,7 @@ impl HttpBackend {
/// Extract an archive (tar, zip, etc.)
fn extract_archive(
&self,
tv: &ToolVersion,
dest: &Path,
file_path: &Path,
file_info: &FileInfo,
Expand All @@ -397,6 +410,20 @@ impl HttpBackend {
};

file::untar(file_path, dest, &tar_opts)?;

// Handle rename_exe option for archives
if let Some(rename_to) = get_opt(opts, "rename_exe") {
let search_dir = if let Some(bin_path_template) = get_opt(opts, "bin_path") {
let bin_path = template_string(&bin_path_template, tv);
dest.join(&bin_path)
} else {
dest.to_path_buf()
};
Comment thread
cursor[bot] marked this conversation as resolved.
// rsplit('/') always yields at least one element (the full string if no delimiter)
let tool_name = self.ba.tool_name.rsplit('/').next().unwrap();
rename_executable_in_dir(&search_dir, &rename_to, Some(tool_name))?;
}

Ok(ExtractionType::Archive)
}

Expand Down Expand Up @@ -574,6 +601,7 @@ pub fn install_time_option_keys() -> Vec<String> {
"version_json_path".into(),
"version_expr".into(),
"format".into(),
"rename_exe".into(),
]
}

Expand Down Expand Up @@ -680,7 +708,14 @@ impl Backend for HttpBackend {
self.extraction_type_from_cache(&cache_key, &file_info)
} else {
ctx.pr.set_message("extracting to cache".into());
self.extract_to_cache(&file_path, &cache_key, &url, &opts, Some(ctx.pr.as_ref()))?
self.extract_to_cache(
&tv,
&file_path,
&cache_key,
&url,
&opts,
Some(ctx.pr.as_ref()),
)?
};

// Create symlinks
Expand Down
2 changes: 1 addition & 1 deletion src/backend/static_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ fn should_skip_file(file_name: &str, strict: bool) -> bool {
/// - `tool_name`: Optional hint for finding non-executable files by name matching.
/// When provided, if no executable is found, will search for files matching the tool name
/// and make them executable before renaming.
fn rename_executable_in_dir(
pub fn rename_executable_in_dir(
dir: &Path,
new_name: &str,
tool_name: Option<&str>,
Expand Down
Loading