Add Linux musl CLI builds (#9240)

Signed-off-by: jh-block <jhugo@block.xyz>
This commit is contained in:
jh-block
2026-05-19 09:28:20 +02:00
committed by GitHub
parent 49dd575789
commit bdb7d214e7
9 changed files with 169 additions and 44 deletions
+11 -1
View File
@@ -69,7 +69,15 @@ clap_complete_nushell = "4.6.0"
winapi = { workspace = true }
[features]
default = ["code-mode", "local-inference", "aws-providers", "telemetry", "otel", "rustls-tls"]
default = [
"code-mode",
"local-inference",
"aws-providers",
"telemetry",
"otel",
"rustls-tls",
"system-keyring",
]
code-mode = ["goose/code-mode"]
local-inference = ["goose/local-inference"]
aws-providers = ["goose/aws-providers"]
@@ -77,6 +85,8 @@ cuda = ["goose/cuda", "local-inference"]
vulkan = ["goose/vulkan", "local-inference"]
telemetry = ["goose/telemetry"]
otel = ["goose/otel"]
system-keyring = ["goose/system-keyring"]
portable-default = ["rustls-tls", "aws-providers", "telemetry", "otel"]
# disables the update command
disable-update = []
rustls-tls = [
+10 -2
View File
@@ -18,14 +18,22 @@ fn asset_name() -> &'static str {
{
"goose-x86_64-apple-darwin.tar.bz2"
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[cfg(all(target_os = "linux", target_arch = "x86_64", target_env = "gnu"))]
{
"goose-x86_64-unknown-linux-gnu.tar.bz2"
}
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
#[cfg(all(target_os = "linux", target_arch = "aarch64", target_env = "gnu"))]
{
"goose-aarch64-unknown-linux-gnu.tar.bz2"
}
#[cfg(all(target_os = "linux", target_arch = "x86_64", target_env = "musl"))]
{
"goose-x86_64-unknown-linux-musl.tar.bz2"
}
#[cfg(all(target_os = "linux", target_arch = "aarch64", target_env = "musl"))]
{
"goose-aarch64-unknown-linux-musl.tar.bz2"
}
#[cfg(all(target_os = "windows", target_arch = "x86_64", feature = "cuda"))]
{
"goose-x86_64-pc-windows-msvc-cuda.zip"
+11 -1
View File
@@ -12,7 +12,15 @@ description.workspace = true
workspace = true
[features]
default = ["code-mode", "local-inference", "aws-providers", "telemetry", "otel", "rustls-tls"]
default = [
"code-mode",
"local-inference",
"aws-providers",
"telemetry",
"otel",
"rustls-tls",
"system-keyring",
]
code-mode = ["goose/code-mode"]
local-inference = ["goose/local-inference"]
aws-providers = ["goose/aws-providers"]
@@ -20,6 +28,8 @@ cuda = ["goose/cuda", "local-inference"]
vulkan = ["goose/vulkan", "local-inference"]
telemetry = ["goose/telemetry"]
otel = ["goose/otel"]
system-keyring = ["goose/system-keyring"]
portable-default = ["rustls-tls", "aws-providers", "telemetry", "otel"]
rustls-tls = [
"reqwest/rustls",
"tokio-tungstenite/rustls-tls-native-roots",
+15 -5
View File
@@ -9,7 +9,15 @@ repository.workspace = true
description.workspace = true
[features]
default = ["code-mode", "local-inference", "aws-providers", "telemetry", "otel", "rustls-tls"]
default = [
"code-mode",
"local-inference",
"aws-providers",
"telemetry",
"otel",
"rustls-tls",
"system-keyring",
]
telemetry = []
otel = [
"dep:tracing-opentelemetry",
@@ -59,6 +67,8 @@ native-tls = [
"oauth2/reqwest",
"oauth2/native-tls",
]
system-keyring = ["dep:keyring"]
portable-default = ["rustls-tls", "aws-providers", "telemetry", "otel"]
[lints]
@@ -110,7 +120,7 @@ opentelemetry_sdk = { workspace = true, optional = true }
opentelemetry-appender-tracing = { workspace = true, optional = true }
opentelemetry-otlp = { workspace = true, optional = true }
opentelemetry-stdout = { workspace = true, optional = true }
keyring = { version = "3.6.2", features = ["vendored"] }
keyring = { version = "3.6.2", features = ["vendored"], optional = true }
serde_yaml = { workspace = true }
strum = { workspace = true }
once_cell = { workspace = true }
@@ -203,17 +213,17 @@ rustls = { version = "0.23", features = ["aws_lc_rs"], optional = true }
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { workspace = true }
keyring = { version = "3.6.2", features = ["windows-native"] }
keyring = { version = "3.6.2", features = ["windows-native"], optional = true }
# Platform-specific GPU acceleration for Whisper and local inference
[target.'cfg(target_os = "macos")'.dependencies]
candle-core = { version = "0.10.2", default-features = false, features = ["metal"], optional = true }
candle-nn = { version = "0.10.2", default-features = false, features = ["metal"], optional = true }
llama-cpp-2 = { version = "0.1.145", features = ["sampler", "metal", "mtmd"], optional = true }
keyring = { version = "3.6.2", features = ["apple-native"] }
keyring = { version = "3.6.2", features = ["apple-native"], optional = true }
[target.'cfg(target_os = "linux")'.dependencies]
keyring = { version = "3.6.2", features = ["sync-secret-service"] }
keyring = { version = "3.6.2", features = ["sync-secret-service"], optional = true }
libc = "0.2.186"
[dev-dependencies]
+65 -29
View File
@@ -1,6 +1,7 @@
use crate::config::paths::Paths;
use crate::config::GooseMode;
use fs2::FileExt;
#[cfg(feature = "system-keyring")]
use keyring::Entry;
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
@@ -34,7 +35,9 @@ fn write_secrets_file(path: &Path, content: &str) -> std::io::Result<()> {
}
}
#[cfg(feature = "system-keyring")]
const KEYRING_SERVICE: &str = "goose";
#[cfg(feature = "system-keyring")]
const KEYRING_USERNAME: &str = "secrets";
pub const CONFIG_YAML_NAME: &str = "config.yaml";
@@ -68,6 +71,7 @@ impl From<serde_yaml::Error> for ConfigError {
}
}
#[cfg(feature = "system-keyring")]
impl From<keyring::Error> for ConfigError {
fn from(err: keyring::Error) -> Self {
ConfigError::KeyringError(err.to_string())
@@ -131,8 +135,13 @@ pub struct Config {
}
enum SecretStorage {
Keyring { service: String },
File { path: PathBuf },
#[cfg(feature = "system-keyring")]
Keyring {
service: String,
},
File {
path: PathBuf,
},
}
// Global instance
@@ -175,19 +184,11 @@ impl Default for Config {
secrets_cache: Arc::new(Mutex::new(None)),
};
let secrets = if env::var("GOOSE_DISABLE_KEYRING").is_ok()
let keyring_disabled = env::var("GOOSE_DISABLE_KEYRING").is_ok()
|| no_secrets_config
.get_param::<serde_yaml::Value>("GOOSE_DISABLE_KEYRING")
.is_ok_and(|v| keyring_disabled_value(&v))
{
SecretStorage::File {
path: config_dir.join("secrets.yaml"),
}
} else {
SecretStorage::Keyring {
service: KEYRING_SERVICE.to_string(),
}
};
.is_ok_and(|v| keyring_disabled_value(&v));
let secrets = secret_storage(&config_dir, keyring_disabled, default_keyring_service());
Self {
config_paths,
secrets,
@@ -350,6 +351,40 @@ fn keyring_disabled_in_config(config_path: &Path) -> bool {
.unwrap_or(false)
}
#[cfg(feature = "system-keyring")]
fn default_keyring_service() -> &'static str {
KEYRING_SERVICE
}
#[cfg(not(feature = "system-keyring"))]
fn default_keyring_service() -> &'static str {
""
}
fn secrets_file_path_in(config_dir: &Path) -> PathBuf {
config_dir.join("secrets.yaml")
}
#[cfg(feature = "system-keyring")]
fn secret_storage(config_dir: &Path, keyring_disabled: bool, service: &str) -> SecretStorage {
if keyring_disabled {
SecretStorage::File {
path: secrets_file_path_in(config_dir),
}
} else {
SecretStorage::Keyring {
service: service.to_string(),
}
}
}
#[cfg(not(feature = "system-keyring"))]
fn secret_storage(config_dir: &Path, _keyring_disabled: bool, _service: &str) -> SecretStorage {
SecretStorage::File {
path: secrets_file_path_in(config_dir),
}
}
impl Config {
/// Get the global configuration instance.
///
@@ -365,21 +400,13 @@ impl Config {
/// to manage multiple configuration files.
pub fn new<P: AsRef<Path>>(config_path: P, service: &str) -> Result<Self, ConfigError> {
let config_path = config_path.as_ref().to_path_buf();
let secrets = if env::var("GOOSE_DISABLE_KEYRING").is_ok()
|| keyring_disabled_in_config(&config_path)
{
let config_dir = config_path
.parent()
.map(Path::to_path_buf)
.unwrap_or_else(Paths::config_dir);
SecretStorage::File {
path: config_dir.join("secrets.yaml"),
}
} else {
SecretStorage::Keyring {
service: service.to_string(),
}
};
let keyring_disabled =
env::var("GOOSE_DISABLE_KEYRING").is_ok() || keyring_disabled_in_config(&config_path);
let config_dir = config_path
.parent()
.map(Path::to_path_buf)
.unwrap_or_else(Paths::config_dir);
let secrets = secret_storage(&config_dir, keyring_disabled, service);
Ok(Config {
config_paths: vec![config_path],
secrets,
@@ -598,6 +625,7 @@ impl Config {
tracing::debug!("secrets cache miss, fetching from storage");
let loaded = match &self.secrets {
#[cfg(feature = "system-keyring")]
SecretStorage::Keyring { service } => {
let result =
self.handle_keyring_operation(|entry| entry.get_password(), service, None);
@@ -857,6 +885,7 @@ impl Config {
fn write_all_secrets(&self, values: &HashMap<String, Value>) -> Result<(), ConfigError> {
match &self.secrets {
#[cfg(feature = "system-keyring")]
SecretStorage::Keyring { service } => {
let json_value = serde_json::to_string(values)?;
match self.handle_keyring_operation(
@@ -975,17 +1004,20 @@ impl Config {
}
/// Get the path to the secrets storage file
#[cfg(feature = "system-keyring")]
fn secrets_file_path() -> PathBuf {
Paths::config_dir().join("secrets.yaml")
secrets_file_path_in(&Paths::config_dir())
}
/// Fall back to file storage when keyring is unavailable
#[cfg(feature = "system-keyring")]
fn fallback_to_file_storage(&self) -> Result<HashMap<String, Value>, ConfigError> {
let path = Self::secrets_file_path();
self.read_secrets_from_file(&path)
}
/// Write secrets to file storage (used for fallback)
#[cfg(feature = "system-keyring")]
fn write_secrets_to_file(&self, values: &HashMap<String, Value>) -> Result<(), ConfigError> {
std::fs::create_dir_all(Paths::config_dir())?;
let path = Self::secrets_file_path();
@@ -1000,6 +1032,7 @@ impl Config {
}
/// Check if an error string indicates a keyring availability issue that should trigger fallback
#[cfg(feature = "system-keyring")]
fn is_keyring_availability_error(&self, error_str: &str) -> bool {
let lower = error_str.to_lowercase();
lower.contains("keyring")
@@ -1010,11 +1043,13 @@ impl Config {
}
/// Get a keyring entry for the specified service
#[cfg(feature = "system-keyring")]
fn get_keyring_entry(service: &str) -> Result<keyring::Entry, keyring::Error> {
Entry::new(service, KEYRING_USERNAME)
}
/// Handle keyring errors with automatic fallback to file storage
#[cfg(feature = "system-keyring")]
fn handle_keyring_fallback_error<T>(
&self,
keyring_err: &keyring::Error,
@@ -1036,6 +1071,7 @@ impl Config {
}
/// Handle keyring operation with automatic fallback to file storage
#[cfg(feature = "system-keyring")]
fn handle_keyring_operation<T>(
&self,
operation: impl FnOnce(keyring::Entry) -> Result<T, keyring::Error>,
+3
View File
@@ -31,6 +31,7 @@
//!
use super::errors::ProviderError;
#[cfg(feature = "local-inference")]
use super::local_inference::LOCAL_LLM_MODEL_CONFIG_KEY;
use super::ollama::OLLAMA_DEFAULT_PORT;
use super::ollama::OLLAMA_HOST;
@@ -52,6 +53,8 @@ use uuid::Uuid;
pub const DEFAULT_INTERPRETER_MODEL_OLLAMA: &str = "mistral-nemo";
pub const TOOLSHIM_BACKEND_ENV_VAR: &str = "GOOSE_TOOLSHIM_BACKEND";
pub const TOOLSHIM_LOCAL_MODEL_ENV_VAR: &str = "GOOSE_TOOLSHIM_MODEL";
#[cfg(not(feature = "local-inference"))]
const LOCAL_LLM_MODEL_CONFIG_KEY: &str = "LOCAL_LLM_MODEL";
const TOOL_CALLS_SECTION_BEGIN: &str = "<|tool_calls_section_begin|>";
const TOOL_CALLS_SECTION_END: &str = "<|tool_calls_section_end|>";