diff --git a/docs/dev-tools/backends/github.md b/docs/dev-tools/backends/github.md index 33221d22ec..773b31cd04 100644 --- a/docs/dev-tools/backends/github.md +++ b/docs/dev-tools/backends/github.md @@ -153,6 +153,21 @@ bin = "docker-compose" # Rename the downloaded binary to docker-compose 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 after extraction from an archive. This is useful when the archive contains a binary with a platform-specific name that you want to rename: + +```toml +[tools."github:yt-dlp/yt-dlp"] +version = "latest" +asset_pattern = "yt-dlp_linux.zip" +rename_exe = "yt-dlp" # Rename the extracted binary to yt-dlp +``` + +::: tip +Use `rename_exe` for archives where the binary inside has a different name than desired. Use `bin` for single binary downloads (non-archives). +::: + ### `bin_path` Specify the directory containing binaries within the extracted archive, or where to place the downloaded file. This supports templating with `{name}`, `{version}`, `{os}`, `{arch}`, and `{ext}`: diff --git a/registry.toml b/registry.toml index aa6b0cc66f..9e62553f9f 100644 --- a/registry.toml +++ b/registry.toml @@ -1491,7 +1491,7 @@ backends = ["asdf:mise-plugins/mise-elixir-ls"] description = "A frontend-independent IDE 'smartness' server for Elixir. Implements the 'Language Server Protocol' standard and provides debugger support via the 'Debug Adapter Protocol'" [tools.elm] -backends = ["ubi:elm/compiler[exe=elm]", "asdf:asdf-community/asdf-elm"] +backends = ["github:elm/compiler[bin=elm]", "asdf:asdf-community/asdf-elm"] description = "Compiler for Elm, a functional language for reliable webapps" test = ["elm --version", "{{version}}"] @@ -3446,7 +3446,10 @@ backends = ["aqua:open-policy-agent/opa", "asdf:tochukwuvictor/asdf-opa"] description = "Open Policy Agent (OPA) is an open source, general-purpose policy engine" [tools.opam] -backends = ["ubi:ocaml/opam", "asdf:asdf-community/asdf-opam"] +backends = [ + "github:ocaml/opam[asset_pattern=opam-*-{gnu_arch}-{os}]", + "asdf:asdf-community/asdf-opam", +] description = "(ocaml) opam is a source-based package manager. It supports multiple simultaneous compiler installations, flexible package constraints, and a Git-friendly development workflow" test = ["opam --version", "{{version}}"] @@ -5226,7 +5229,7 @@ description = "yq is a portable command-line YAML processor" test = ["yq --version", "version v{{version}}"] [tools.yt-dlp] -backends = ["ubi:yt-dlp/yt-dlp[rename_exe=yt-dlp]", "asdf:duhow/asdf-yt-dlp"] +backends = ["github:yt-dlp/yt-dlp[rename_exe=yt-dlp]", "asdf:duhow/asdf-yt-dlp"] description = "A feature-rich command-line audio/video downloader" test = ["yt-dlp --version", "{{version}}"] diff --git a/src/backend/github.rs b/src/backend/github.rs index b1f71ff6ee..bd668652ab 100644 --- a/src/backend/github.rs +++ b/src/backend/github.rs @@ -654,6 +654,11 @@ fn template_string_for_target(template: &str, tv: &ToolVersion, target: &Platfor "arm64" => "aarch64", _ => arch, }; + // GNU-style arch: x64 -> x86_64, arm64 stays arm64 (used by opam, etc.) + let gnu_arch = match arch { + "x64" => "x86_64", + _ => arch, + }; template .replace("{version}", version) @@ -663,6 +668,7 @@ fn template_string_for_target(template: &str, tv: &ToolVersion, target: &Platfor .replace("{darwin_os}", darwin_os) .replace("{amd64_arch}", amd64_arch) .replace("{x86_64_arch}", x86_64_arch) + .replace("{gnu_arch}", gnu_arch) } #[cfg(test)] diff --git a/src/backend/static_helpers.rs b/src/backend/static_helpers.rs index f568c8dfff..eb9afe9b6a 100644 --- a/src/backend/static_helpers.rs +++ b/src/backend/static_helpers.rs @@ -345,6 +345,13 @@ pub fn install_artifact( // Extract with determined strip_components file::untar(file_path, &install_path, &tar_opts)?; + + // Handle rename_exe option for archives + if let Some(rename_to) = + lookup_platform_key(opts, "rename_exe").or_else(|| opts.get("rename_exe").cloned()) + { + rename_executable_in_dir(&install_path, &rename_to)?; + } } Ok(()) } @@ -393,6 +400,38 @@ pub fn verify_checksum_str( Ok(()) } +/// Renames the first executable file found in a directory to a new name. +/// Used by the `rename_exe` option to rename binaries after archive extraction. +fn rename_executable_in_dir(dir: &Path, new_name: &str) -> eyre::Result<()> { + let target_path = dir.join(new_name); + + // Check if target already exists before iterating + // (read_dir order is non-deterministic, so we must check first) + if target_path.is_file() && crate::file::is_executable(&target_path) { + return Ok(()); + } + + // Find executables in the directory (non-recursive for top level) + for entry in std::fs::read_dir(dir)?.flatten() { + let path = entry.path(); + if path.is_file() && crate::file::is_executable(&path) { + let file_name = path.file_name().unwrap().to_string_lossy(); + // Skip common non-binary files + if file_name.starts_with('.') + || file_name.ends_with(".txt") + || file_name.ends_with(".md") + { + continue; + } + // Rename this executable + std::fs::rename(&path, &target_path)?; + debug!("Renamed {} to {}", path.display(), target_path.display()); + return Ok(()); + } + } + Ok(()) +} + /// Cleans a binary name by removing OS/arch suffixes and version numbers. /// This is useful when downloading single binaries that have platform-specific names. /// Executable extensions (.exe, .bat, .sh, etc.) are preserved.