From c9bfca4826da2e9453fe944762f2178c9bdae1ec Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 13 Feb 2026 17:57:30 +0100 Subject: [PATCH] feat: add maven settings.xml support for java private registries (#7940) Co-authored-by: Claude Opus 4.6 --- backend/src/main.rs | 5 +-- backend/src/monitor.rs | 35 +++++++++++++++++-- .../windmill-common/src/global_settings.rs | 1 + .../windmill-worker/src/csharp_executor.rs | 8 +++-- backend/windmill-worker/src/rust_executor.rs | 8 +++-- backend/windmill-worker/src/worker.rs | 1 + .../lib/components/InstanceSettings.svelte | 6 +++- .../src/lib/components/instanceSettings.ts | 10 ++++++ 8 files changed, 64 insertions(+), 10 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index 83fe5b7a23..323b7dd627 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -10,7 +10,7 @@ use monitor::{ load_base_url, load_otel, reload_critical_alerts_on_db_oversize, reload_delete_logs_periodically_setting, reload_indexer_config, reload_instance_python_version_setting, reload_maven_repos_setting, - reload_no_default_maven_setting, reload_nuget_config_setting, + reload_maven_settings_xml_setting, reload_no_default_maven_setting, reload_nuget_config_setting, reload_powershell_repo_pat_setting, reload_powershell_repo_url_setting, reload_ruby_repos_setting, reload_timeout_wait_result_setting, send_current_log_file_to_object_store, send_logs_to_object_store, WORKERS_NAMES, @@ -44,7 +44,7 @@ use windmill_common::{ JOB_ISOLATION_SETTING, HUB_API_SECRET_SETTING, HUB_BASE_URL_SETTING, INDEXER_SETTING, INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING, - MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NO_DEFAULT_MAVEN_SETTING, + MAVEN_SETTINGS_XML_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NO_DEFAULT_MAVEN_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING, OTEL_SETTING, OTEL_TRACING_PROXY_SETTING, PIP_INDEX_URL_SETTING, UV_INDEX_STRATEGY_SETTING, POWERSHELL_REPO_PAT_SETTING, POWERSHELL_REPO_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING, @@ -1582,6 +1582,7 @@ async fn process_notify_event( POWERSHELL_REPO_URL_SETTING => reload_powershell_repo_url_setting(conn).await, POWERSHELL_REPO_PAT_SETTING => reload_powershell_repo_pat_setting(conn).await, MAVEN_REPOS_SETTING => reload_maven_repos_setting(conn).await, + MAVEN_SETTINGS_XML_SETTING => reload_maven_settings_xml_setting(conn).await, NO_DEFAULT_MAVEN_SETTING => reload_no_default_maven_setting(conn).await, RUBY_REPOS_SETTING => reload_ruby_repos_setting(conn).await, HUB_API_SECRET_SETTING => reload_hub_api_secret_setting(conn).await, diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index e8bbe5c08b..1ed06b1cf8 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -88,8 +88,9 @@ use windmill_queue::{cancel_job, get_queued_job_v2, SameWorkerPayload}; use windmill_worker::{ result_processor::handle_job_error, JobCompletedSender, JobIsolationLevel, OtelTracingProxySettings, SameWorkerSender, BUNFIG_INSTALL_SCOPES, CARGO_REGISTRIES, - INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, JOB_ISOLATION, KEEP_JOB_DIR, MAVEN_REPOS, - NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NSJAIL_AVAILABLE, NUGET_CONFIG, + INSTANCE_PYTHON_VERSION, JAVA_HOME_DIR, JOB_DEFAULT_TIMEOUT, JOB_ISOLATION, KEEP_JOB_DIR, + MAVEN_REPOS, MAVEN_SETTINGS_XML, NO_DEFAULT_MAVEN, NPM_CONFIG_REGISTRY, NSJAIL_AVAILABLE, + NUGET_CONFIG, OTEL_TRACING_PROXY_SETTINGS, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, POWERSHELL_REPO_PAT, POWERSHELL_REPO_URL, UV_INDEX_STRATEGY, }; @@ -329,6 +330,7 @@ pub async fn initial_load( reload_powershell_repo_url_setting(&conn).await; reload_powershell_repo_pat_setting(&conn).await; reload_maven_repos_setting(&conn).await; + reload_maven_settings_xml_setting(&conn).await; reload_no_default_maven_setting(&conn).await; reload_ruby_repos_setting(&conn).await; reload_cargo_registries_setting(&conn).await; @@ -1336,6 +1338,35 @@ pub async fn reload_maven_repos_setting(conn: &Connection) { .await; } +pub async fn reload_maven_settings_xml_setting(conn: &Connection) { + reload_option_setting_with_tracing( + conn, + windmill_common::global_settings::MAVEN_SETTINGS_XML_SETTING, + "MAVEN_SETTINGS_XML", + MAVEN_SETTINGS_XML.clone(), + ) + .await; + + let settings_xml = MAVEN_SETTINGS_XML.read().await.clone(); + match settings_xml { + Some(ref content) if !content.trim().is_empty() => { + let m2_dir = format!("{JAVA_HOME_DIR}/.m2"); + if let Err(e) = tokio::fs::create_dir_all(&m2_dir).await { + tracing::error!("Failed to create .m2 directory: {e:#}"); + return; + } + let settings_path = format!("{m2_dir}/settings.xml"); + if let Err(e) = tokio::fs::write(&settings_path, content).await { + tracing::error!("Failed to write Maven settings.xml: {e:#}"); + } + } + _ => { + let settings_path = format!("{JAVA_HOME_DIR}/.m2/settings.xml"); + let _ = tokio::fs::remove_file(&settings_path).await; + } + } +} + pub async fn reload_no_default_maven_setting(conn: &Connection) { let value = load_value_from_global_settings_with_conn( conn, diff --git a/backend/windmill-common/src/global_settings.rs b/backend/windmill-common/src/global_settings.rs index cfa0839214..93a20e9041 100644 --- a/backend/windmill-common/src/global_settings.rs +++ b/backend/windmill-common/src/global_settings.rs @@ -14,6 +14,7 @@ pub const NUGET_CONFIG_SETTING: &str = "nuget_config"; pub const POWERSHELL_REPO_URL_SETTING: &str = "powershell_repo_url"; pub const POWERSHELL_REPO_PAT_SETTING: &str = "powershell_repo_pat"; pub const MAVEN_REPOS_SETTING: &str = "maven_repos"; +pub const MAVEN_SETTINGS_XML_SETTING: &str = "maven_settings_xml"; pub const NO_DEFAULT_MAVEN_SETTING: &str = "no_default_maven"; pub const RUBY_REPOS_SETTING: &str = "ruby_repos"; pub const CARGO_REGISTRIES_SETTING: &str = "cargo_registries"; diff --git a/backend/windmill-worker/src/csharp_executor.rs b/backend/windmill-worker/src/csharp_executor.rs index 49cbd2af5a..015a34fd7f 100644 --- a/backend/windmill-worker/src/csharp_executor.rs +++ b/backend/windmill-worker/src/csharp_executor.rs @@ -83,7 +83,9 @@ pub async fn generate_nuget_lockfile( check_executor_binary_exists("dotnet", DOTNET_PATH.as_str(), "C#")?; if let Some(nuget_config) = NUGET_CONFIG.read().await.clone() { - write_file(job_dir, "nuget.config", &nuget_config)?; + if !nuget_config.trim().is_empty() { + write_file(job_dir, "nuget.config", &nuget_config)?; + } } let (reqs, lines_to_remove) = parse_csharp_reqs(code); @@ -335,7 +337,9 @@ async fn build_cs_proj( occupancy_metrics: &mut OccupancyMetrics, ) -> error::Result { if let Some(nuget_config) = NUGET_CONFIG.read().await.clone() { - write_file(job_dir, "nuget.config", &nuget_config)?; + if !nuget_config.trim().is_empty() { + write_file(job_dir, "nuget.config", &nuget_config)?; + } } let mut build_cs_cmd = Command::new(DOTNET_PATH.as_str()); diff --git a/backend/windmill-worker/src/rust_executor.rs b/backend/windmill-worker/src/rust_executor.rs index 7b1bf7f820..c5a36c6a67 100644 --- a/backend/windmill-worker/src/rust_executor.rs +++ b/backend/windmill-worker/src/rust_executor.rs @@ -232,9 +232,11 @@ pub fn __WINDMILL_RUN__(_args: __WINDMILL_ARGS__) -> Result anyhow::Result<()> { if let Some(cargo_registries) = CARGO_REGISTRIES.read().await.clone() { - let cargo_dir = format!("{job_dir}/.cargo"); - create_dir_all(&cargo_dir).await?; - write_file(&cargo_dir, "config.toml", &cargo_registries)?; + if !cargo_registries.trim().is_empty() { + let cargo_dir = format!("{job_dir}/.cargo"); + create_dir_all(&cargo_dir).await?; + write_file(&cargo_dir, "config.toml", &cargo_registries)?; + } } Ok(()) } diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 00535dbd17..d3005b0441 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -579,6 +579,7 @@ lazy_static::lazy_static! { pub static ref POWERSHELL_REPO_URL: Arc>> = Arc::new(RwLock::new(None)); pub static ref POWERSHELL_REPO_PAT: Arc>> = Arc::new(RwLock::new(None)); pub static ref MAVEN_REPOS: Arc>> = Arc::new(RwLock::new(None)); + pub static ref MAVEN_SETTINGS_XML: Arc>> = Arc::new(RwLock::new(None)); pub static ref NO_DEFAULT_MAVEN: AtomicBool = AtomicBool::new(std::env::var("NO_DEFAULT_MAVEN") .ok() .and_then(|x| x.parse::().ok()) diff --git a/frontend/src/lib/components/InstanceSettings.svelte b/frontend/src/lib/components/InstanceSettings.svelte index 24b41f8bcc..74d4032b8c 100644 --- a/frontend/src/lib/components/InstanceSettings.svelte +++ b/frontend/src/lib/components/InstanceSettings.svelte @@ -353,9 +353,13 @@ .map(async (x) => { if (x.key === 'license_key') licenseKeySet = true if (x.requiresReloadOnChange) shouldReloadPage = true + let value = $values?.[x.key] + if (x.fieldType === 'codearea' && typeof value === 'string' && value.trim() === '') { + value = undefined + } return await SettingService.setGlobal({ key: x.key, - requestBody: { value: $values?.[x.key] } + requestBody: { value } }) }) ) diff --git a/frontend/src/lib/components/instanceSettings.ts b/frontend/src/lib/components/instanceSettings.ts index c4b702e8f1..87170fd39f 100644 --- a/frontend/src/lib/components/instanceSettings.ts +++ b/frontend/src/lib/components/instanceSettings.ts @@ -442,6 +442,16 @@ export const settings: Record = { storage: 'setting', ee_only: '' }, + { + label: 'Maven settings.xml', + description: + 'Write a Maven settings.xml file for custom repositories, mirrors, and credentials', + key: 'maven_settings_xml', + fieldType: 'codearea', + codeAreaLang: 'xml', + storage: 'setting', + ee_only: '' + }, { label: 'Disable default Maven repository', description: 'Do not use default Maven repository',