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
36 changes: 36 additions & 0 deletions src/backend/platform_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,39 @@ impl PlatformTarget {
self.platform.to_key()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_platform_target_creation() {
let platform = Platform::parse("linux-x64").unwrap();
let target = PlatformTarget::new(platform.clone());

assert_eq!(target.platform, platform);
assert_eq!(target.os_name(), "linux");
assert_eq!(target.arch_name(), "x64");
assert_eq!(target.qualifier(), None);
assert_eq!(target.to_key(), "linux-x64");
}

#[test]
fn test_platform_target_with_qualifier() {
let platform = Platform::parse("linux-x64-musl").unwrap();
let target = PlatformTarget::new(platform);

assert_eq!(target.os_name(), "linux");
assert_eq!(target.arch_name(), "x64");
assert_eq!(target.qualifier(), Some("musl"));
assert_eq!(target.to_key(), "linux-x64-musl");
}

#[test]
fn test_from_current() {
let target = PlatformTarget::from_current();
let current_platform = Platform::current();

assert_eq!(target.platform, current_platform);
}
}
121 changes: 94 additions & 27 deletions src/plugins/core/bun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ use crate::http::HTTP;
use crate::install_context::InstallContext;
use crate::toolset::ToolVersion;
use crate::ui::progress_report::SingleReport;
use crate::{backend::Backend, config::Config};
use crate::{
backend::{Backend, GitHubReleaseInfo, ReleaseType, platform_target::PlatformTarget},
config::Config,
};
use crate::{file, github, plugins};

#[derive(Debug)]
Expand Down Expand Up @@ -116,44 +119,108 @@ impl Backend for BunPlugin {

Ok(tv)
}
}

fn os() -> &'static str {
if cfg!(target_os = "macos") {
"darwin"
} else if cfg!(target_os = "linux") {
"linux"
} else {
&OS
// ========== Lockfile Metadata Fetching Implementation ==========

async fn get_github_release_info(
&self,
tv: &ToolVersion,
target: &PlatformTarget,
) -> Result<Option<GitHubReleaseInfo>> {
let version = &tv.version;

// Build the asset pattern for Bun's GitHub releases
// Pattern: bun-{os}-{arch}.zip (where arch may include variants like -musl, -baseline)
let os_name = Self::map_os_to_bun(target.os_name());
let arch_name = Self::get_bun_arch_for_target(target);
let asset_pattern = format!("bun-{os_name}-{arch_name}.zip");

Ok(Some(GitHubReleaseInfo {
repo: "oven-sh/bun".to_string(),
asset_pattern: Some(asset_pattern),
api_url: Some(format!(
"https://github.com/oven-sh/bun/releases/download/bun-v{version}"
)),
release_type: ReleaseType::GitHub,
}))
}
Comment thread
cursor[bot] marked this conversation as resolved.
}

fn arch() -> &'static str {
if cfg!(target_arch = "x86_64") {
if cfg!(target_env = "musl") {
if cfg!(target_feature = "avx2") {
"x64-musl"
} else {
"x64-musl-baseline"
impl BunPlugin {
/// Map our platform OS names to Bun's naming convention
fn map_os_to_bun(os: &str) -> &str {
match os {
"macos" => "darwin",
"linux" => "linux",
"windows" => "windows",
other => other,
}
}

/// Map our platform arch names to Bun's naming convention
/// Note: This handles simple cases. Complex musl/baseline variants are handled in arch()
fn map_arch_to_bun(arch: &str) -> &str {
match arch {
"x64" => "x64",
"arm64" | "aarch64" => "aarch64",
other => other,
}
}

/// Get the full Bun arch string for a target platform
/// This handles musl, baseline, and other variants based on platform qualifiers
fn get_bun_arch_for_target(target: &PlatformTarget) -> String {
let base_arch = Self::map_arch_to_bun(target.arch_name());

// Handle qualifiers like musl, baseline, etc.
if let Some(qualifier) = target.qualifier() {
match qualifier {
"musl" => format!("{}-musl", base_arch),
"musl-baseline" => format!("{}-musl-baseline", base_arch),
"baseline" => format!("{}-baseline", base_arch),
other => format!("{}-{}", base_arch, other),
}
} else if cfg!(target_feature = "avx2") {
"x64"
} else {
"x64-baseline"
base_arch.to_string()
}
} else if cfg!(target_arch = "aarch64") {
if cfg!(target_env = "musl") {
"aarch64-musl"
} else if cfg!(windows) {
"x64"
}

/// Get the full Bun arch string with variants (musl, baseline, etc.)
fn get_bun_arch_with_variants() -> &'static str {
if cfg!(target_arch = "x86_64") {
if cfg!(target_env = "musl") {
if cfg!(target_feature = "avx2") {
"x64-musl"
} else {
"x64-musl-baseline"
}
} else if cfg!(target_feature = "avx2") {
"x64"
} else {
"x64-baseline"
}
} else if cfg!(target_arch = "aarch64") {
if cfg!(target_env = "musl") {
"aarch64-musl"
} else if cfg!(windows) {
"x64"
} else {
"aarch64"
}
} else {
"aarch64"
&ARCH
}
} else {
&ARCH
}
}

fn os() -> &'static str {
BunPlugin::map_os_to_bun(&OS)
}

fn arch() -> &'static str {
BunPlugin::get_bun_arch_with_variants()
}

fn bun_bin_name() -> &'static str {
if cfg!(windows) { "bun.exe" } else { "bun" }
}
103 changes: 75 additions & 28 deletions src/plugins/core/node.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::backend::{Backend, VersionCacheManager};
use crate::backend::{Backend, VersionCacheManager, platform_target::PlatformTarget};
use crate::build_time::built_info;
use crate::cache::CacheManagerBuilder;
use crate::cli::args::BackendArg;
Expand Down Expand Up @@ -534,6 +534,75 @@ impl Backend for NodePlugin {
})
.clone()
}

// ========== Lockfile Metadata Fetching Implementation ==========
Comment on lines +537 to +538

Copilot AI Sep 7, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Remove the extra blank line before the comment to maintain consistent spacing with the rest of the file.

Copilot uses AI. Check for mistakes.

async fn get_tarball_url(
&self,
tv: &ToolVersion,
target: &PlatformTarget,
) -> Result<Option<String>> {
let version = &tv.version;
let settings = Settings::get();

// Build platform-specific filename like Node.js does
let slug = self.build_platform_slug(version, target);
let filename = if target.os_name() == "windows" {
format!("{slug}.zip")
} else {
format!("{slug}.tar.gz")
};

// Use Node.js mirror URL to construct download URL
let url = settings
.node
.mirror_url()
.join(&format!("v{version}/{filename}"))
.map_err(|e| eyre::eyre!("Failed to construct Node.js download URL: {e}"))?;

Ok(Some(url.to_string()))
}
}

impl NodePlugin {
/// Map OS name from Platform to Node.js convention
fn map_os(os_name: &str) -> &str {
match os_name {
"macos" => "darwin",
"linux" => "linux",
"windows" => "win",
other => other,

Copilot AI Sep 7, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Consider logging a warning when encountering unknown OS names to help with debugging platform mapping issues.

Suggested change
other => other,
other => {
eprintln!(
"Warning: Unknown OS name '{}' encountered in Node.js platform mapping. Passing through as-is.",
other
);
other
},

Copilot uses AI. Check for mistakes.
}
}

/// Map arch name from Platform to Node.js convention
fn map_arch(arch_name: &str) -> &str {
match arch_name {
"x86" => "x86",
"x64" => "x64",
"arm" => "armv7l",
"arm64" => "arm64",
"aarch64" => "arm64",
"loongarch64" => "loong64",
"riscv64" => "riscv64",
other => other,

Copilot AI Sep 7, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Consider logging a warning when encountering unknown architecture names to help with debugging platform mapping issues.

Suggested change
other => other,
other => {
eprintln!(
"Warning: Unknown architecture name '{}' encountered in build_platform_slug for target: {:?}. Using as-is.",
other,
target
);
other
},

Copilot uses AI. Check for mistakes.
}
}

/// Build platform-specific slug for Node.js downloads
/// This mirrors the logic from BuildOpts::new() and slug() function
fn build_platform_slug(&self, version: &str, target: &PlatformTarget) -> String {
let settings = Settings::get();

let os = Self::map_os(target.os_name());
let arch = Self::map_arch(target.arch_name());

if let Some(flavor) = &settings.node.flavor {
format!("node-v{version}-{os}-{arch}-{flavor}")
} else {
format!("node-v{version}-{os}-{arch}")
}
}
}

#[derive(Debug)]
Expand Down Expand Up @@ -620,38 +689,16 @@ fn make_install_cmd() -> String {
}

fn os() -> &'static str {
if cfg!(target_os = "linux") {
"linux"
} else if cfg!(target_os = "macos") {
"darwin"
} else if cfg!(target_os = "windows") {
"win"
} else {
built_info::CFG_OS
}
NodePlugin::map_os(built_info::CFG_OS)
}

fn arch(settings: &Settings) -> &str {
let arch = settings.arch();
if arch == "x86" {
"x86"
} else if arch == "x64" {
"x64"
} else if arch == "arm" {
if cfg!(target_feature = "v6") {
"armv6l"
} else {
"armv7l"
}
} else if arch == "loongarch64" {
"loong64"
} else if arch == "riscv64" {
"riscv64"
} else if arch == "aarch64" {
"arm64"
} else {
arch
// Special handling for ARM with target features
if arch == "arm" && cfg!(target_feature = "v6") {
return "armv6l";
}
NodePlugin::map_arch(arch)
}

fn slug(v: &str) -> String {
Expand Down
Loading