Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions src/bin/julialauncher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
};
use juliaup::global_paths::get_paths;
use juliaup::jsonstructs_versionsdb::JuliaupVersionDB;
use juliaup::operations::{is_pr_channel, is_valid_channel};

Check warning on line 11 in src/bin/julialauncher.rs

View workflow job for this annotation

GitHub Actions / Rustfmt Check

Diff in /home/runner/work/juliaup/juliaup/src/bin/julialauncher.rs
use juliaup::utils::{print_juliaup_style, JuliaupMessageType};
use juliaup::versions_file::load_versions_db;
use std::collections::HashMap;
#[cfg(not(windows))]
use nix::{
sys::wait::{waitpid, WaitStatus},
unistd::{fork, ForkResult},

Check warning on line 18 in src/bin/julialauncher.rs

View workflow job for this annotation

GitHub Actions / Rustfmt Check

Diff in /home/runner/work/juliaup/juliaup/src/bin/julialauncher.rs
};
use normpath::PathExt;
#[cfg(not(windows))]
Expand Down Expand Up @@ -553,6 +554,64 @@
}
}

/// Process Julia environment variables:
/// 1. Check current environment for each Julia env var
/// 2. If set in current env (and non-empty), use it and persist to config
/// 3. If not set in current env but exists in persisted config, use persisted value
/// Returns a HashMap of environment variables to set for the Julia process

Check failure on line 561 in src/bin/julialauncher.rs

View workflow job for this annotation

GitHub Actions / Clippy Check (windows-latest)

doc list item without indentation

Check failure on line 561 in src/bin/julialauncher.rs

View workflow job for this annotation

GitHub Actions / Clippy Check (ubuntu-latest)

doc list item without indentation

Check failure on line 561 in src/bin/julialauncher.rs

View workflow job for this annotation

GitHub Actions / Clippy Check (macos-latest)

doc list item without indentation
fn process_julia_environment_variables(
paths: &juliaup::global_paths::GlobalPaths,
) -> Result<HashMap<String, String>> {
use juliaup::utils::get_julia_environment_variables;

let mut env_vars_to_set = HashMap::new();
let mut config_needs_update = false;

// Load config for reading persisted env vars
let config_file = load_config_db(paths, None)
.with_context(|| "Failed to load config when processing environment variables.")?;

let persisted_vars = &config_file.data.settings.julia_env_variables;

// Collect environment variables that need to be persisted
let mut vars_to_persist = HashMap::new();

for var_name in get_julia_environment_variables() {
// Check if variable is set in current environment
if let Ok(current_value) = std::env::var(var_name) {
if !current_value.is_empty() {
// Use the current environment value
env_vars_to_set.insert(var_name.to_string(), current_value.clone());

// Check if we need to persist this value
if persisted_vars.get(var_name) != Some(&current_value) {
vars_to_persist.insert(var_name.to_string(), current_value);
config_needs_update = true;
}
}
} else if let Some(persisted_value) = persisted_vars.get(var_name) {
// Not set in current environment, but we have a persisted value
env_vars_to_set.insert(var_name.to_string(), persisted_value.clone());
}
}

Check warning on line 597 in src/bin/julialauncher.rs

View workflow job for this annotation

GitHub Actions / Rustfmt Check

Diff in /home/runner/work/juliaup/juliaup/src/bin/julialauncher.rs
// If we need to update config, do it now
if config_needs_update {
let mut config_mut = load_mut_config_db(paths)
.with_context(|| "Failed to load mutable config for persisting environment variables.")?;

// Update the persisted environment variables
for (key, value) in vars_to_persist {
config_mut.data.settings.julia_env_variables.insert(key, value);

Check warning on line 605 in src/bin/julialauncher.rs

View workflow job for this annotation

GitHub Actions / Rustfmt Check

Diff in /home/runner/work/juliaup/juliaup/src/bin/julialauncher.rs
}

save_config_db(&mut config_mut)
.with_context(|| "Failed to save config after persisting environment variables.")?;
}

Ok(env_vars_to_set)
}

fn run_app() -> Result<i32> {
if std::io::stdout().is_terminal() {
// Set console title
Expand Down Expand Up @@ -624,6 +683,10 @@
}
}

// Process Julia environment variables: load persisted values and persist current non-empty values
let julia_env_vars = process_julia_environment_variables(&paths)
.with_context(|| "Failed to process Julia environment variables.")?;

// On *nix platforms we replace the current process with the Julia one.
// This simplifies use in e.g. debuggers, but requires that we fork off
// a subprocess to do the selfupdate and versiondb update.
Expand Down Expand Up @@ -652,6 +715,7 @@
// replace the current process
let _ = std::process::Command::new(&julia_path)
.args(&new_args)
.envs(&julia_env_vars)
.exec();

// this is only ever reached if launching Julia fails
Expand Down Expand Up @@ -727,6 +791,7 @@

let mut child_process = std::process::Command::new(julia_path)
.args(&new_args)
.envs(&julia_env_vars)
.spawn()
.with_context(|| "The Julia launcher failed to start Julia.")?; // TODO Maybe include the command we actually tried to start?

Expand Down
9 changes: 9 additions & 0 deletions src/config_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ pub struct JuliaupConfigSettings {
skip_serializing_if = "Option::is_none"
)]
pub auto_install_channels: Option<bool>,
#[serde(
rename = "JuliaEnvironmentVariables",
default,
skip_serializing_if = "HashMap::is_empty"
)]
pub julia_env_variables: HashMap<String, String>,
}

impl Default for JuliaupConfigSettings {
Expand All @@ -90,6 +96,7 @@ impl Default for JuliaupConfigSettings {
create_channel_symlinks: false,
versionsdb_update_interval: default_versionsdb_update_interval(),
auto_install_channels: None,
julia_env_variables: HashMap::new(),
}
}
}
Expand Down Expand Up @@ -221,6 +228,7 @@ pub fn load_config_db(
create_channel_symlinks: false,
versionsdb_update_interval: default_versionsdb_update_interval(),
auto_install_channels: None,
julia_env_variables: HashMap::new(),
},
last_version_db_update: None,
},
Expand Down Expand Up @@ -322,6 +330,7 @@ pub fn load_mut_config_db(paths: &GlobalPaths) -> Result<JuliaupConfigFile> {
create_channel_symlinks: false,
versionsdb_update_interval: default_versionsdb_update_interval(),
auto_install_channels: None,
julia_env_variables: HashMap::new(),
},
last_version_db_update: None,
};
Expand Down
141 changes: 141 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,79 @@
assert_eq!(p, "x64");
assert_eq!(v, Version::new(1, 1, 1));
}

#[test]
fn test_get_julia_environment_variables() {
let env_vars = get_julia_environment_variables();

Check warning on line 160 in src/utils.rs

View workflow job for this annotation

GitHub Actions / Rustfmt Check

Diff in /home/runner/work/juliaup/juliaup/src/utils.rs

// Should return a non-empty list
assert!(!env_vars.is_empty(), "Should have Julia environment variables");

// Should contain common variables
assert!(
env_vars.contains(&"JULIA_NUM_THREADS"),
"Should include JULIA_NUM_THREADS"
);
assert!(
env_vars.contains(&"JULIA_DEPOT_PATH"),
"Should include JULIA_DEPOT_PATH"
);
assert!(
env_vars.contains(&"JULIA_EDITOR"),
"Should include JULIA_EDITOR"
);
assert!(
env_vars.contains(&"JULIA_PKG_SERVER"),
"Should include JULIA_PKG_SERVER"
);

// Should NOT contain JULIA_PROJECT (it's explicitly excluded)
assert!(
!env_vars.contains(&"JULIA_PROJECT"),
"Should NOT include JULIA_PROJECT"
);

// All entries should start with "JULIA_" or be known exceptions
for var in &env_vars {
assert!(
var.starts_with("JULIA_")
|| *var == "NO_COLOR"
|| *var == "FORCE_COLOR"
|| *var == "ENABLE_JITPROFILING"
|| *var == "ENABLE_GDBLISTENER",
"Variable {} should start with JULIA_ or be a known exception",
var
);
}

// Check that we have variables from different categories
assert!(
env_vars.contains(&"JULIA_NUM_THREADS"),
"Should have parallelization vars"
);
assert!(
env_vars.contains(&"JULIA_ERROR_COLOR"),
"Should have REPL formatting vars"
);
assert!(
env_vars.contains(&"JULIA_DEBUG"),
"Should have debugging vars"
);
}

#[test]
fn test_julia_environment_variables_uniqueness() {
let env_vars = get_julia_environment_variables();
let mut seen = std::collections::HashSet::new();

for var in env_vars {
assert!(
seen.insert(var),
"Duplicate environment variable found: {}",
var
);
}
}
}

// Message formatting constants and functions
Expand Down Expand Up @@ -223,3 +296,71 @@

eprintln!("{} {}", styled_action, message);
}

/// Returns the list of Julia environment variables that can be persisted
/// Excludes JULIA_PROJECT as noted in the Julia documentation
pub fn get_julia_environment_variables() -> Vec<&'static str> {
vec![
// File Locations
"JULIA_BINDIR",
"JULIA_LOAD_PATH",
"JULIA_DEPOT_PATH",
"JULIA_HISTORY",
"JULIA_MAX_NUM_PRECOMPILE_FILES",
"JULIA_VERBOSE_LINKING",
// Pkg.jl
"JULIA_CI",
"JULIA_NUM_PRECOMPILE_TASKS",
"JULIA_PKG_DEVDIR",
"JULIA_PKG_IGNORE_HASHES",
"JULIA_PKG_OFFLINE",
"JULIA_PKG_PRECOMPILE_AUTO",
"JULIA_PKG_SERVER",
"JULIA_PKG_SERVER_REGISTRY_PREFERENCE",
"JULIA_PKG_UNPACK_REGISTRY",
"JULIA_PKG_USE_CLI_GIT",
"JULIA_PKGRESOLVE_ACCURACY",
"JULIA_PKG_PRESERVE_TIERED_INSTALLED",
"JULIA_PKG_GC_AUTO",
// Network Transport
"JULIA_NO_VERIFY_HOSTS",
"JULIA_SSL_NO_VERIFY_HOSTS",
"JULIA_SSH_NO_VERIFY_HOSTS",
"JULIA_ALWAYS_VERIFY_HOSTS",
"JULIA_SSL_CA_ROOTS_PATH",
// External Applications
"JULIA_SHELL",
"JULIA_EDITOR",
// Parallelization
"JULIA_CPU_THREADS",
"JULIA_WORKER_TIMEOUT",
"JULIA_NUM_THREADS",
"JULIA_THREAD_SLEEP_THRESHOLD",
"JULIA_NUM_GC_THREADS",
"JULIA_IMAGE_THREADS",
"JULIA_IMAGE_TIMINGS",
"JULIA_EXCLUSIVE",
// Garbage Collection
"JULIA_HEAP_SIZE_HINT",
// REPL Formatting
"JULIA_ERROR_COLOR",
"JULIA_WARN_COLOR",
"JULIA_INFO_COLOR",
"JULIA_INPUT_COLOR",
"JULIA_ANSWER_COLOR",
"NO_COLOR",
"FORCE_COLOR",
// System and Package Image Building
"JULIA_CPU_TARGET",
// Debugging and Profiling
"JULIA_DEBUG",
"JULIA_PROFILE_PEEK_HEAP_SNAPSHOT",
"JULIA_TIMING_SUBSYSTEMS",
"JULIA_GC_NO_GENERATIONAL",
"JULIA_GC_WAIT_FOR_DEBUGGER",
"ENABLE_JITPROFILING",
"ENABLE_GDBLISTENER",
"JULIA_LLVM_ARGS",
"JULIA_FALLBACK_REPL",
]
}
Loading
Loading