fix: forge URL derivation for bare IP hosts, add Cargo registry config

forge_url() now checks active context domain first before falling back
to production_host. Bare IP addresses are skipped in the host heuristic.
Adds .cargo/config.toml for the sunbeam Gitea Cargo registry.
This commit is contained in:
2026-03-21 15:17:47 +00:00
parent 46d21330b1
commit 31fde1a8c6
5 changed files with 24 additions and 12 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[registries.sunbeam]
index = "sparse+https://src.sunbeam.pt/api/packages/studio/cargo/"

3
.gitignore vendored
View File

@@ -8,3 +8,6 @@ build/
# Rust # Rust
/target/ /target/
# Environment
.envrc

View File

@@ -29,20 +29,22 @@ fn forge_url() -> String {
return url.trim_end_matches('/').to_string(); return url.trim_end_matches('/').to_string();
} }
// Derive from production_host domain in config // Derive from active context domain or production_host
let domain = crate::config::domain();
if !domain.is_empty() {
return format!("https://src.{domain}");
}
let config = crate::config::load_config(); let config = crate::config::load_config();
if !config.production_host.is_empty() { if !config.production_host.is_empty() {
// production_host is like "user@server.example.com" — extract domain
let host = config let host = config
.production_host .production_host
.split('@') .split('@')
.last() .last()
.unwrap_or(&config.production_host); .unwrap_or(&config.production_host);
// Strip any leading subdomain segments that look like a hostname to get the base domain. // For "admin.sunbeam.pt" → "sunbeam.pt". Skip bare IPs.
// For a host like "admin.sunbeam.pt", the forge is "src.sunbeam.pt".
// Heuristic: use the last two segments as the domain.
let parts: Vec<&str> = host.split('.').collect(); let parts: Vec<&str> = host.split('.').collect();
if parts.len() >= 2 { if parts.len() >= 2 && parts.iter().any(|p| p.parse::<u8>().is_err()) {
let domain = format!("{}.{}", parts[parts.len() - 2], parts[parts.len() - 1]); let domain = format!("{}.{}", parts[parts.len() - 2], parts[parts.len() - 1]);
return format!("https://src.{domain}"); return format!("https://src.{domain}");
} }

View File

@@ -3,6 +3,9 @@ name = "sunbeam-sdk"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
description = "Sunbeam SDK — reusable library for cluster management" description = "Sunbeam SDK — reusable library for cluster management"
repository = "https://src.sunbeam.pt/studio/cli"
license = "MIT"
publish = ["sunbeam"]
[features] [features]
default = [] default = []

View File

@@ -29,20 +29,22 @@ fn forge_url() -> String {
return url.trim_end_matches('/').to_string(); return url.trim_end_matches('/').to_string();
} }
// Derive from production_host domain in config // Derive from active context domain or production_host
let domain = crate::config::domain();
if !domain.is_empty() {
return format!("https://src.{domain}");
}
let config = crate::config::load_config(); let config = crate::config::load_config();
if !config.production_host.is_empty() { if !config.production_host.is_empty() {
// production_host is like "user@server.example.com" — extract domain
let host = config let host = config
.production_host .production_host
.split('@') .split('@')
.last() .last()
.unwrap_or(&config.production_host); .unwrap_or(&config.production_host);
// Strip any leading subdomain segments that look like a hostname to get the base domain. // For "admin.sunbeam.pt" → "sunbeam.pt". Skip bare IPs.
// For a host like "admin.sunbeam.pt", the forge is "src.sunbeam.pt".
// Heuristic: use the last two segments as the domain.
let parts: Vec<&str> = host.split('.').collect(); let parts: Vec<&str> = host.split('.').collect();
if parts.len() >= 2 { if parts.len() >= 2 && parts.iter().any(|p| p.parse::<u8>().is_err()) {
let domain = format!("{}.{}", parts[parts.len() - 2], parts[parts.len() - 1]); let domain = format!("{}.{}", parts[parts.len() - 2], parts[parts.len() - 1]);
return format!("https://src.{domain}"); return format!("https://src.{domain}");
} }