Skip to content

Commit 7be073d

Browse files
authored
Merge pull request #13 from sdairs/issue-12-linux-install-support
Add Linux install support
2 parents dee2f33 + 8309559 commit 7be073d

5 files changed

Lines changed: 119 additions & 9 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "chv"
3-
version = "0.1.7"
3+
version = "0.1.8"
44
edition = "2024"
55

66
[dependencies]

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ pub enum Error {
4040
#[error("Failed to execute ClickHouse: {0}")]
4141
Exec(String),
4242

43+
#[error("Extraction failed: {0}")]
44+
Extract(String),
45+
4346
#[error("Cloud API error: {0}")]
4447
Cloud(String),
4548
}

src/version_manager/install.rs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::error::{Error, Result};
22
use crate::paths;
33
use crate::version_manager::download::download_version;
4+
use crate::version_manager::resolve::is_tarball_download;
45
use std::os::unix::fs::PermissionsExt;
56

67
/// Installs a ClickHouse version
@@ -17,11 +18,21 @@ pub async fn install_version(version: &str, channel: &str) -> Result<()> {
1718
// Create the version directory
1819
std::fs::create_dir_all(&version_dir)?;
1920

20-
// Download the binary directly to the destination
2121
let binary_path = version_dir.join("clickhouse");
2222

2323
println!("Downloading ClickHouse {}...", version);
24-
download_version(version, channel, &binary_path).await?;
24+
25+
if is_tarball_download()? {
26+
// Linux: download tarball, extract, move binary
27+
let tarball_path = version_dir.join("clickhouse.tgz");
28+
download_version(version, channel, &tarball_path).await?;
29+
30+
println!("Extracting...");
31+
extract_tarball(&tarball_path, &version_dir, version)?;
32+
} else {
33+
// macOS: download binary directly
34+
download_version(version, channel, &binary_path).await?;
35+
}
2536

2637
// Make the binary executable
2738
let mut perms = std::fs::metadata(&binary_path)?.permissions();
@@ -31,3 +42,47 @@ pub async fn install_version(version: &str, channel: &str) -> Result<()> {
3142
println!("ClickHouse {} installed successfully", version);
3243
Ok(())
3344
}
45+
46+
/// Extracts the ClickHouse binary from a Linux tarball
47+
fn extract_tarball(
48+
tarball_path: &std::path::Path,
49+
version_dir: &std::path::Path,
50+
version: &str,
51+
) -> Result<()> {
52+
// Extract the tarball
53+
let status = std::process::Command::new("tar")
54+
.args(["xzf", &tarball_path.to_string_lossy()])
55+
.current_dir(version_dir)
56+
.status()
57+
.map_err(|e| Error::Extract(format!("Failed to run tar: {}", e)))?;
58+
59+
if !status.success() {
60+
// Clean up tarball on failure
61+
let _ = std::fs::remove_file(tarball_path);
62+
return Err(Error::Extract("tar extraction failed".to_string()));
63+
}
64+
65+
// Find the clickhouse binary inside the extracted directory
66+
let extracted_dir = version_dir.join(format!("clickhouse-common-static-{}", version));
67+
let extracted_binary = extracted_dir.join("usr/bin/clickhouse");
68+
69+
if !extracted_binary.exists() {
70+
// Clean up on failure
71+
let _ = std::fs::remove_file(tarball_path);
72+
let _ = std::fs::remove_dir_all(&extracted_dir);
73+
return Err(Error::Extract(format!(
74+
"Binary not found at expected path: {}",
75+
extracted_binary.display()
76+
)));
77+
}
78+
79+
// Move binary to final location
80+
let final_binary = version_dir.join("clickhouse");
81+
std::fs::rename(&extracted_binary, &final_binary)?;
82+
83+
// Clean up tarball and extracted directory
84+
let _ = std::fs::remove_file(tarball_path);
85+
let _ = std::fs::remove_dir_all(&extracted_dir);
86+
87+
Ok(())
88+
}

src/version_manager/resolve.rs

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,35 @@ pub async fn resolve_version(version_spec: &str) -> Result<VersionEntry> {
8181
}
8282
}
8383

84+
/// Returns whether the current platform downloads a tarball (Linux) vs a bare binary (macOS)
85+
pub fn is_tarball_download() -> Result<bool> {
86+
let (os, _) = detect_platform()?;
87+
Ok(os == "linux")
88+
}
89+
8490
/// Builds the download URL for a specific version from GitHub releases
85-
/// URL format: https://github.com/ClickHouse/ClickHouse/releases/download/v{version}-{channel}/clickhouse-{os}-{arch}
91+
/// macOS: .../clickhouse-macos-{arch}
92+
/// Linux: .../clickhouse-common-static-{version}-{arch}.tgz
8693
pub fn build_download_url(version: &str, channel: &str) -> Result<String> {
8794
let (os, arch) = detect_platform()?;
88-
Ok(format!(
89-
"https://github.com/ClickHouse/ClickHouse/releases/download/v{}-{}/clickhouse-{}-{}",
90-
version, channel, os, arch
91-
))
95+
let base = format!(
96+
"https://github.com/ClickHouse/ClickHouse/releases/download/v{}-{}",
97+
version, channel
98+
);
99+
match os {
100+
"linux" => {
101+
let linux_arch = match arch {
102+
"x86_64" => "amd64",
103+
"aarch64" => "arm64",
104+
_ => arch,
105+
};
106+
Ok(format!(
107+
"{}/clickhouse-common-static-{}-{}.tgz",
108+
base, version, linux_arch
109+
))
110+
}
111+
_ => Ok(format!("{}/clickhouse-{}-{}", base, os, arch)),
112+
}
92113
}
93114

94115
#[cfg(test)]
@@ -117,4 +138,35 @@ mod tests {
117138
assert!(url.starts_with("https://github.com/ClickHouse/ClickHouse/releases/download/"));
118139
assert!(url.contains("v25.8.16.34-lts"));
119140
}
141+
142+
#[test]
143+
fn test_build_download_url_platform_specific() {
144+
let url = build_download_url("25.12.5.44", "stable").unwrap();
145+
let (os, arch) = detect_platform().unwrap();
146+
match os {
147+
"macos" => {
148+
assert!(url.contains(&format!("clickhouse-macos-{}", arch)));
149+
assert!(!url.ends_with(".tgz"));
150+
}
151+
"linux" => {
152+
let expected_arch = match arch {
153+
"x86_64" => "amd64",
154+
"aarch64" => "arm64",
155+
_ => arch,
156+
};
157+
assert!(url.contains(&format!(
158+
"clickhouse-common-static-25.12.5.44-{}.tgz",
159+
expected_arch
160+
)));
161+
}
162+
_ => panic!("unexpected os: {}", os),
163+
}
164+
}
165+
166+
#[test]
167+
fn test_is_tarball_download() {
168+
let is_tarball = is_tarball_download().unwrap();
169+
let (os, _) = detect_platform().unwrap();
170+
assert_eq!(is_tarball, os == "linux");
171+
}
120172
}

0 commit comments

Comments
 (0)