Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
20 changes: 19 additions & 1 deletion 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 @@ -397,6 +397,23 @@ 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) = get_opt(opts, "bin_path") {
dest.join(&bin_path)
} else {
dest.to_path_buf()
};
Comment thread
cursor[bot] marked this conversation as resolved.
let tool_name = self
.ba
.tool_name
.rsplit('/')
.next()
.unwrap_or(&self.ba.tool_name);

Copilot AI Jan 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rsplit('/') logic extracts the last segment after a forward slash, but the fallback to self.ba.tool_name when next() returns None is unreachable. The rsplit iterator always returns at least one element (the entire string if no delimiter is found), so next() will never return None. Consider removing the unwrap_or or adding a comment explaining why it's kept for defensive programming.

Suggested change
.unwrap_or(&self.ba.tool_name);
// `rsplit` on a non-empty string always yields at least one element
.expect("rsplit('/') on tool_name should always yield at least one segment");

Copilot uses AI. Check for mistakes.
rename_executable_in_dir(&search_dir, &rename_to, Some(tool_name))?;
}

Ok(ExtractionType::Archive)
}

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

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