From 5f22d11124bae1d1357aada296cb6cb8b7547c2c Mon Sep 17 00:00:00 2001 From: Cole Mickens Date: Thu, 1 Aug 2024 08:50:39 -0700 Subject: [PATCH] uds2: move merge_nix_configs to shared --- src/cli/cmd/login/mod.rs | 147 +++----------------------------------- src/cli/cmd/mod.rs | 4 +- src/cli/cmd/status/mod.rs | 2 +- src/shared/mod.rs | 135 ++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 142 deletions(-) diff --git a/src/cli/cmd/login/mod.rs b/src/cli/cmd/login/mod.rs index 5250e5f8..c398a154 100644 --- a/src/cli/cmd/login/mod.rs +++ b/src/cli/cmd/login/mod.rs @@ -96,7 +96,7 @@ impl LoginSubcommand { // $XDG_CONFIG_HOME/nix/nix.conf; basically ~/.config/nix/nix.conf let nix_config_path = xdg.place_config_file("nix/nix.conf")?; // $XDG_CONFIG_HOME/fh/auth; basically ~/.config/fh/auth - let token_path = auth_token_path()?; + let token_path = user_auth_token_path()?; let dnixd_state_dir = Path::new(&DETERMINATE_STATE_DIR); let netrc_file_path: PathBuf = dnixd_state_dir.join(DETERMINATE_NIXD_NETRC_NAME); @@ -270,7 +270,7 @@ impl LoginSubcommand { // TODO(cole-h): make this atomic -- copy the nix_config_path to some temporary file, then operate // on that, then move it back if all is good -async fn upsert_user_nix_config( +pub async fn upsert_user_nix_config( nix_config_path: &Path, netrc_file_string: &str, netrc_contents: &str, @@ -369,8 +369,11 @@ async fn upsert_user_nix_config( .await { Ok(mut file) => { - let nix_config_contents = - merge_nix_configs(nix_config, nix_config_contents, merged_nix_config); + let nix_config_contents = crate::shared::merge_nix_configs( + nix_config, + nix_config_contents, + merged_nix_config, + ); let write_status = file.write_all(nix_config_contents.as_bytes()).await; Some(write_status.is_ok()) } @@ -399,144 +402,10 @@ async fn upsert_user_nix_config( Ok(()) } -pub(crate) fn auth_token_path() -> Result { +pub(crate) fn user_auth_token_path() -> Result { let xdg = xdg::BaseDirectories::new()?; // $XDG_CONFIG_HOME/flakehub/auth; basically ~/.config/flakehub/auth let token_path = xdg.place_config_file("flakehub/auth")?; Ok(token_path) } - -// NOTE(cole-h): Adapted from -// https://github.com/DeterminateSystems/nix-installer/blob/0b0172547c4666f6b1eacb6561a59d6b612505a3/src/action/base/create_or_merge_nix_config.rs#L284 -const NIX_CONF_COMMENT_CHAR: char = '#'; -fn merge_nix_configs( - mut existing_nix_config: nix_config_parser::NixConfig, - mut existing_nix_config_contents: String, - mut merged_nix_config: nix_config_parser::NixConfig, -) -> String { - let mut new_config = String::new(); - - // We append a newline to ensure that, in the case there are comments at the end of the - // file and _NO_ trailing newline, we still preserve the entire block of comments. - existing_nix_config_contents.push('\n'); - - let (associated_lines, _, _) = existing_nix_config_contents.split('\n').fold( - (Vec::new(), Vec::new(), false), - |(mut all_assoc, mut current_assoc, mut associating): ( - Vec>, - Vec, - bool, - ), - line| { - let line = line.trim(); - - if line.starts_with(NIX_CONF_COMMENT_CHAR) { - associating = true; - } else if line.is_empty() || !line.starts_with(NIX_CONF_COMMENT_CHAR) { - associating = false; - } - - current_assoc.push(line.to_string()); - - if !associating { - all_assoc.push(current_assoc); - current_assoc = Vec::new(); - } - - (all_assoc, current_assoc, associating) - }, - ); - - for line_group in associated_lines { - if line_group.is_empty() || line_group.iter().all(|line| line.is_empty()) { - continue; - } - - // This expect should never reasonably panic, because we would need a line group - // consisting solely of a comment and nothing else, but unconditionally appending a - // newline to the config string before grouping above prevents this from occurring. - let line_idx = line_group - .iter() - .position(|line| !line.starts_with(NIX_CONF_COMMENT_CHAR)) - .expect("There should always be one line without a comment character"); - - let setting_line = &line_group[line_idx]; - let comments = line_group[..line_idx].join("\n"); - - // If we're here, but the line without a comment char is empty, we have - // standalone comments to preserve, but no settings with inline comments. - if setting_line.is_empty() { - for line in &line_group { - new_config.push_str(line); - new_config.push('\n'); - } - - continue; - } - - // Preserve inline comments for settings we've merged - let to_remove = if let Some((name, value)) = existing_nix_config - .settings() - .iter() - .find(|(name, _value)| setting_line.starts_with(*name)) - { - new_config.push_str(&comments); - new_config.push('\n'); - new_config.push_str(name); - new_config.push_str(" = "); - - if let Some(merged_value) = merged_nix_config.settings_mut().shift_remove(name) { - new_config.push_str(&merged_value); - new_config.push(' '); - } else { - new_config.push_str(value); - } - - if let Some(inline_comment_idx) = setting_line.find(NIX_CONF_COMMENT_CHAR) { - let inline_comment = &setting_line[inline_comment_idx..]; - new_config.push_str(inline_comment); - new_config.push('\n'); - } - - Some(name.clone()) - } else { - new_config.push_str(&comments); - new_config.push('\n'); - new_config.push_str(setting_line); - new_config.push('\n'); - - None - }; - - if let Some(to_remove) = to_remove { - existing_nix_config.settings_mut().shift_remove(&to_remove); - } - } - - // Add the leftover existing nix config - for (name, value) in existing_nix_config.settings() { - if merged_nix_config.settings().get(name).is_some() { - continue; - } - - new_config.push_str(name); - new_config.push_str(" = "); - new_config.push_str(value); - new_config.push('\n'); - } - - new_config.push('\n'); - - for (name, value) in merged_nix_config.settings() { - new_config.push_str(name); - new_config.push_str(" = "); - new_config.push_str(value); - new_config.push('\n'); - } - - new_config - .strip_prefix('\n') - .unwrap_or(&new_config) - .to_owned() -} diff --git a/src/cli/cmd/mod.rs b/src/cli/cmd/mod.rs index 38630548..2482a9b6 100644 --- a/src/cli/cmd/mod.rs +++ b/src/cli/cmd/mod.rs @@ -341,13 +341,13 @@ async fn make_base_client(_authenticated: bool) -> Result { #[cfg(not(test))] async fn make_base_client(authenticated: bool) -> Result { - use self::login::auth_token_path; + use self::login::user_auth_token_path; let mut headers = HeaderMap::new(); headers.insert(ACCEPT, HeaderValue::from_static("application/json")); if authenticated { - if let Ok(token) = tokio::fs::read_to_string(auth_token_path()?).await { + if let Ok(token) = tokio::fs::read_to_string(user_auth_token_path()?).await { if !token.is_empty() { headers.insert( AUTHORIZATION, diff --git a/src/cli/cmd/status/mod.rs b/src/cli/cmd/status/mod.rs index 20859f2e..93f1998d 100644 --- a/src/cli/cmd/status/mod.rs +++ b/src/cli/cmd/status/mod.rs @@ -72,7 +72,7 @@ impl CommandExecute for StatusSubcommand { pub(crate) async fn get_status_from_auth_file( api_addr: url::Url, ) -> color_eyre::Result { - let auth_token_path = crate::cli::cmd::login::auth_token_path()?; + let auth_token_path = crate::cli::cmd::login::user_auth_token_path()?; let token = tokio::fs::read_to_string(&auth_token_path) .await .wrap_err_with(|| format!("Could not open {}", auth_token_path.display()))?; diff --git a/src/shared/mod.rs b/src/shared/mod.rs index 5ff566e4..cd5b9328 100644 --- a/src/shared/mod.rs +++ b/src/shared/mod.rs @@ -22,6 +22,7 @@ pub async fn update_netrc_file( .await .wrap_err("failed to update netrc file contents") } + pub fn netrc_contents( frontend_addr: &url::Url, backend_addr: &url::Url, @@ -47,3 +48,137 @@ pub fn netrc_contents( ); Ok(contents) } + +// NOTE(cole-h): Adapted from +// https://github.com/DeterminateSystems/nix-installer/blob/0b0172547c4666f6b1eacb6561a59d6b612505a3/src/action/base/create_or_merge_nix_config.rs#L284 +const NIX_CONF_COMMENT_CHAR: char = '#'; +pub fn merge_nix_configs( + mut existing_nix_config: nix_config_parser::NixConfig, + mut existing_nix_config_contents: String, + mut merged_nix_config: nix_config_parser::NixConfig, +) -> String { + let mut new_config = String::new(); + + // We append a newline to ensure that, in the case there are comments at the end of the + // file and _NO_ trailing newline, we still preserve the entire block of comments. + existing_nix_config_contents.push('\n'); + + let (associated_lines, _, _) = existing_nix_config_contents.split('\n').fold( + (Vec::new(), Vec::new(), false), + |(mut all_assoc, mut current_assoc, mut associating): ( + Vec>, + Vec, + bool, + ), + line| { + let line = line.trim(); + + if line.starts_with(NIX_CONF_COMMENT_CHAR) { + associating = true; + } else if line.is_empty() || !line.starts_with(NIX_CONF_COMMENT_CHAR) { + associating = false; + } + + current_assoc.push(line.to_string()); + + if !associating { + all_assoc.push(current_assoc); + current_assoc = Vec::new(); + } + + (all_assoc, current_assoc, associating) + }, + ); + + for line_group in associated_lines { + if line_group.is_empty() || line_group.iter().all(|line| line.is_empty()) { + continue; + } + + // This expect should never reasonably panic, because we would need a line group + // consisting solely of a comment and nothing else, but unconditionally appending a + // newline to the config string before grouping above prevents this from occurring. + let line_idx = line_group + .iter() + .position(|line| !line.starts_with(NIX_CONF_COMMENT_CHAR)) + .expect("There should always be one line without a comment character"); + + let setting_line = &line_group[line_idx]; + let comments = line_group[..line_idx].join("\n"); + + // If we're here, but the line without a comment char is empty, we have + // standalone comments to preserve, but no settings with inline comments. + if setting_line.is_empty() { + for line in &line_group { + new_config.push_str(line); + new_config.push('\n'); + } + + continue; + } + + // Preserve inline comments for settings we've merged + let to_remove = if let Some((name, value)) = existing_nix_config + .settings() + .iter() + .find(|(name, _value)| setting_line.starts_with(*name)) + { + new_config.push_str(&comments); + new_config.push('\n'); + new_config.push_str(name); + new_config.push_str(" = "); + + if let Some(merged_value) = merged_nix_config.settings_mut().shift_remove(name) { + new_config.push_str(&merged_value); + new_config.push(' '); + } else { + new_config.push_str(value); + } + + if let Some(inline_comment_idx) = setting_line.find(NIX_CONF_COMMENT_CHAR) { + let inline_comment = &setting_line[inline_comment_idx..]; + new_config.push_str(inline_comment); + new_config.push('\n'); + } + + Some(name.clone()) + } else { + new_config.push_str(&comments); + new_config.push('\n'); + new_config.push_str(setting_line); + new_config.push('\n'); + + None + }; + + if let Some(to_remove) = to_remove { + existing_nix_config.settings_mut().shift_remove(&to_remove); + } + } + + // Add the leftover existing nix config + for (name, value) in existing_nix_config.settings() { + if merged_nix_config.settings().get(name).is_some() { + continue; + } + + new_config.push_str(name); + new_config.push_str(" = "); + new_config.push_str(value); + new_config.push('\n'); + } + + new_config.push('\n'); + + for (name, value) in merged_nix_config.settings() { + new_config.push_str(name); + new_config.push_str(" = "); + new_config.push_str(value); + new_config.push('\n'); + } + + new_config + .strip_prefix('\n') + .unwrap_or(&new_config) + .to_owned() +}