refactor: workspace scaffolding — sunbeam-sdk + sunbeam binary crate
Convert the single binary crate into a Cargo workspace with two members: sunbeam-sdk (library) and sunbeam (thin binary). Moves build.rs to the SDK with adjusted .git/HEAD path for the nested layout.
This commit is contained in:
66
Cargo.toml
66
Cargo.toml
@@ -1,63 +1,3 @@
|
||||
[package]
|
||||
name = "sunbeam"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
description = "Sunbeam local dev stack manager"
|
||||
|
||||
[dependencies]
|
||||
# Core
|
||||
thiserror = "2"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_yaml = "0.9"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
# Kubernetes
|
||||
kube = { version = "0.99", features = ["client", "runtime", "derive", "ws"] }
|
||||
k8s-openapi = { version = "0.24", features = ["v1_32"] }
|
||||
|
||||
# HTTP + TLS
|
||||
reqwest = { version = "0.12", features = ["json", "rustls-tls", "blocking"] }
|
||||
rustls = { version = "0.23", features = ["ring"] }
|
||||
|
||||
# SSH
|
||||
russh = "0.46"
|
||||
russh-keys = "0.46"
|
||||
|
||||
# Crypto
|
||||
rsa = "0.9"
|
||||
pkcs8 = { version = "0.10", features = ["pem"] }
|
||||
pkcs1 = { version = "0.7", features = ["pem"] }
|
||||
sha2 = "0.10"
|
||||
hmac = "0.12"
|
||||
base64 = "0.22"
|
||||
rand = "0.8"
|
||||
|
||||
# Certificate generation
|
||||
rcgen = "0.14"
|
||||
|
||||
# SMTP
|
||||
lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "tokio1-rustls-tls", "builder", "hostname"] }
|
||||
|
||||
# Archive handling
|
||||
flate2 = "1"
|
||||
tar = "0.4"
|
||||
|
||||
# Async
|
||||
futures = "0.3"
|
||||
tokio-stream = "0.1"
|
||||
|
||||
# Utility
|
||||
tempfile = "3"
|
||||
dirs = "5"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
[build-dependencies]
|
||||
reqwest = { version = "0.12", features = ["blocking", "rustls-tls"] }
|
||||
sha2 = "0.10"
|
||||
flate2 = "1"
|
||||
tar = "0.4"
|
||||
chrono = "0.4"
|
||||
[workspace]
|
||||
members = ["sunbeam-sdk", "sunbeam"]
|
||||
resolver = "3"
|
||||
|
||||
65
sunbeam-sdk/Cargo.toml
Normal file
65
sunbeam-sdk/Cargo.toml
Normal file
@@ -0,0 +1,65 @@
|
||||
[package]
|
||||
name = "sunbeam-sdk"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
description = "Sunbeam SDK — reusable library for cluster management"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
cli = ["clap"]
|
||||
|
||||
[dependencies]
|
||||
# Core
|
||||
thiserror = "2"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
clap = { version = "4", features = ["derive"], optional = true }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_yaml = "0.9"
|
||||
tracing = "0.1"
|
||||
|
||||
# Kubernetes
|
||||
kube = { version = "0.99", features = ["client", "runtime", "derive", "ws"] }
|
||||
k8s-openapi = { version = "0.24", features = ["v1_32"] }
|
||||
|
||||
# HTTP + TLS
|
||||
reqwest = { version = "0.12", features = ["json", "rustls-tls", "blocking"] }
|
||||
|
||||
# SSH
|
||||
russh = "0.46"
|
||||
russh-keys = "0.46"
|
||||
|
||||
# Crypto
|
||||
rsa = "0.9"
|
||||
pkcs8 = { version = "0.10", features = ["pem"] }
|
||||
pkcs1 = { version = "0.7", features = ["pem"] }
|
||||
sha2 = "0.10"
|
||||
hmac = "0.12"
|
||||
base64 = "0.22"
|
||||
rand = "0.8"
|
||||
|
||||
# Certificate generation
|
||||
rcgen = "0.14"
|
||||
|
||||
# SMTP
|
||||
lettre = { version = "0.11", default-features = false, features = ["smtp-transport", "tokio1-rustls-tls", "builder", "hostname"] }
|
||||
|
||||
# Archive handling
|
||||
flate2 = "1"
|
||||
tar = "0.4"
|
||||
|
||||
# Async
|
||||
futures = "0.3"
|
||||
tokio-stream = "0.1"
|
||||
|
||||
# Utility
|
||||
tempfile = "3"
|
||||
dirs = "5"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
|
||||
[build-dependencies]
|
||||
reqwest = { version = "0.12", features = ["blocking", "rustls-tls"] }
|
||||
sha2 = "0.10"
|
||||
flate2 = "1"
|
||||
tar = "0.4"
|
||||
chrono = "0.4"
|
||||
132
sunbeam-sdk/build.rs
Normal file
132
sunbeam-sdk/build.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
use flate2::read::GzDecoder;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use tar::Archive;
|
||||
|
||||
const KUSTOMIZE_VERSION: &str = "v5.8.1";
|
||||
const HELM_VERSION: &str = "v4.1.0";
|
||||
|
||||
fn main() {
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let target = env::var("TARGET").unwrap_or_default();
|
||||
let (os, arch) = parse_target(&target);
|
||||
|
||||
download_and_embed("kustomize", KUSTOMIZE_VERSION, &os, &arch, &out_dir);
|
||||
download_and_embed("helm", HELM_VERSION, &os, &arch, &out_dir);
|
||||
|
||||
// Set version info from git
|
||||
let commit = git_commit_sha();
|
||||
println!("cargo:rustc-env=SUNBEAM_COMMIT={commit}");
|
||||
|
||||
// Build target triple and build date
|
||||
println!("cargo:rustc-env=SUNBEAM_TARGET={target}");
|
||||
let date = chrono::Utc::now().format("%Y-%m-%d").to_string();
|
||||
println!("cargo:rustc-env=SUNBEAM_BUILD_DATE={date}");
|
||||
|
||||
// Rebuild if git HEAD changes (workspace root is two levels up)
|
||||
println!("cargo:rerun-if-changed=../../.git/HEAD");
|
||||
}
|
||||
|
||||
fn parse_target(target: &str) -> (String, String) {
|
||||
let os = if target.contains("darwin") {
|
||||
"darwin"
|
||||
} else if target.contains("linux") {
|
||||
"linux"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"darwin"
|
||||
} else {
|
||||
"linux"
|
||||
};
|
||||
|
||||
let arch = if target.contains("aarch64") || target.contains("arm64") {
|
||||
"arm64"
|
||||
} else if target.contains("x86_64") || target.contains("amd64") {
|
||||
"amd64"
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"arm64"
|
||||
} else {
|
||||
"amd64"
|
||||
};
|
||||
|
||||
(os.to_string(), arch.to_string())
|
||||
}
|
||||
|
||||
fn download_and_embed(tool: &str, version: &str, os: &str, arch: &str, out_dir: &PathBuf) {
|
||||
let dest = out_dir.join(tool);
|
||||
if dest.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let url = match tool {
|
||||
"kustomize" => format!(
|
||||
"https://github.com/kubernetes-sigs/kustomize/releases/download/\
|
||||
kustomize%2F{version}/kustomize_{version}_{os}_{arch}.tar.gz"
|
||||
),
|
||||
"helm" => format!(
|
||||
"https://get.helm.sh/helm-{version}-{os}-{arch}.tar.gz"
|
||||
),
|
||||
_ => panic!("Unknown tool: {tool}"),
|
||||
};
|
||||
|
||||
let extract_path = match tool {
|
||||
"kustomize" => "kustomize".to_string(),
|
||||
"helm" => format!("{os}-{arch}/helm"),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
eprintln!("cargo:warning=Downloading {tool} {version} for {os}/{arch}...");
|
||||
|
||||
let response = reqwest::blocking::get(&url)
|
||||
.unwrap_or_else(|e| panic!("Failed to download {tool}: {e}"));
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.unwrap_or_else(|e| panic!("Failed to read {tool} response: {e}"));
|
||||
|
||||
let decoder = GzDecoder::new(&bytes[..]);
|
||||
let mut archive = Archive::new(decoder);
|
||||
|
||||
for entry in archive.entries().expect("Failed to read tar entries") {
|
||||
let mut entry = entry.expect("Failed to read tar entry");
|
||||
let path = entry
|
||||
.path()
|
||||
.expect("Failed to read entry path")
|
||||
.to_path_buf();
|
||||
if path.to_string_lossy() == extract_path {
|
||||
let mut data = Vec::new();
|
||||
entry
|
||||
.read_to_end(&mut data)
|
||||
.expect("Failed to read binary");
|
||||
fs::write(&dest, &data).expect("Failed to write binary");
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
fs::set_permissions(&dest, fs::Permissions::from_mode(0o755))
|
||||
.expect("Failed to set permissions");
|
||||
}
|
||||
|
||||
eprintln!("cargo:warning=Embedded {tool} ({} bytes)", data.len());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Could not find {extract_path} in {tool} archive");
|
||||
}
|
||||
|
||||
fn git_commit_sha() -> String {
|
||||
Command::new("git")
|
||||
.args(["rev-parse", "--short=8", "HEAD"])
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| {
|
||||
if o.status.success() {
|
||||
Some(String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "unknown".to_string())
|
||||
}
|
||||
18
sunbeam/Cargo.toml
Normal file
18
sunbeam/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "sunbeam"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
description = "Sunbeam local dev stack manager"
|
||||
|
||||
[[bin]]
|
||||
name = "sunbeam"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
sunbeam-sdk = { path = "../sunbeam-sdk", features = ["cli"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
chrono = "0.4"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
rustls = { version = "0.23", features = ["ring"] }
|
||||
Reference in New Issue
Block a user