Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability for bob to add neovim proxy to $PATH #243

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
db0931a
changed: capture config file type and path
MordechaiHadad Nov 3, 2024
1de1bac
added: now bob auto adds neovim bin directory to $PATH if user wants
MordechaiHadad Nov 4, 2024
26bd331
add: can now write source env file to rcfile for non fish shells
MordechaiHadad Nov 4, 2024
7d0a255
chore: formatting
MordechaiHadad Nov 4, 2024
c6fca25
disable copy_env_files on windows
MordechaiHadad Nov 4, 2024
cb30790
chore: fmt again
MordechaiHadad Nov 4, 2024
d8046f3
chore: clippy
MordechaiHadad Nov 4, 2024
2aaa640
fix some messages and prompt
MordechaiHadad Nov 5, 2024
2643b01
chore: formatting
MordechaiHadad Nov 5, 2024
0136823
fix: incorrect appended string
MordechaiHadad Nov 5, 2024
533acf5
fix: no need to convert str to bytes
MordechaiHadad Nov 5, 2024
acb26c9
add: remove nvim-bin path from $PATH on erase command
MordechaiHadad Nov 5, 2024
c1c9a85
fix: clippy
MordechaiHadad Nov 5, 2024
c28cf0c
chore: formatting
MordechaiHadad Nov 5, 2024
cb01a64
added: better explanations of version string for help command in the …
MordechaiHadad Nov 5, 2024
05b703e
chore: formatting
MordechaiHadad Nov 5, 2024
f5d3476
fix: alignment of descriptions for invalid version string
MordechaiHadad Nov 7, 2024
5b6f898
Merge branch 'master' into add/what-the-path
MordechaiHadad Jan 16, 2025
36a0a11
change: fallback to $HOME if zdotenv doesnt exist
MordechaiHadad Jan 16, 2025
bc94bb8
Merge branch 'add/what-the-path' of github.com:MordechaiHadad/bob int…
MordechaiHadad Jan 16, 2025
829af8d
fix: resolve conflics with new path mangement PR by sid
MordechaiHadad Jan 16, 2025
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
1,062 changes: 812 additions & 250 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ clap_complete = "4.1"
toml = "0.8.8"
semver = "1.0.22"
sha2 = "0.10.8"
what-the-path = "^0.1.3"

[dependencies.chrono]
version = "0.4.23"
Expand Down Expand Up @@ -92,7 +93,7 @@ tar = "0.4"

[target."cfg(windows)".dependencies]
winreg = "0.10.1"
zip = "0.5"
zip = "2.2.0"


[[bin]]
Expand Down
4 changes: 4 additions & 0 deletions env/env.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Credit: https://github.com/rust-lang/rustup/blob/master/src/cli/self_update/env.fish
if not contains "{nvim_bin}" $PATH
set -x PATH "{nvim_bin}" $PATH
end
9 changes: 9 additions & 0 deletions env/env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
# Credit: https://github.com/rust-lang/rustup/blob/master/src/cli/self_update/env.sh
case ":${PATH}:" in
*:"{nvim_bin}":*)
;;
*)
export PATH="{nvim_bin}:$PATH"
;;
esac
21 changes: 14 additions & 7 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
config::Config,
config::ConfigFile,
handlers::{
self, erase_handler, list_handler, list_remote_handler, rollback_handler, sync_handler,
uninstall_handler, update_handler, InstallResult,
Expand Down Expand Up @@ -57,6 +57,8 @@ enum Cli {
/// install command if the version is not installed already
Use {
/// Version to switch to |nightly|stable|<version-string>|<commit-hash>|
///
/// A version-string can either be `vx.x.x` or `x.x.x` examples: `v0.6.1` and `0.6.0`
version: String,

/// Whether not to auto-invoke install command
Expand All @@ -68,6 +70,8 @@ enum Cli {
/// out-of-date nightly version
Install {
/// Version to be installed |nightly|stable|<version-string>|<commit-hash>|
///
/// A version-string can either be `vx.x.x` or `x.x.x` examples: `v0.6.1` and `0.6.0`
version: String,
},

Expand All @@ -79,6 +83,9 @@ enum Cli {
#[clap(alias = "remove", visible_alias = "rm")]
Uninstall {
/// Optional Version to be uninstalled |nightly|stable|<version-string>|<commit-hash>|
///
/// A version-string can either be `vx.x.x` or `x.x.x` examples: `v0.6.1` and `0.6.0`
///
/// If no Version is provided a prompt is used to select the versions to be uninstalled
version: Option<String>,
},
Expand Down Expand Up @@ -153,7 +160,7 @@ pub struct Update {
/// let config = Config::default();
/// start(config).await.unwrap();
/// ```
pub async fn start(config: Config) -> Result<()> {
pub async fn start(config: ConfigFile) -> Result<()> {
let client = create_reqwest_client()?;
let cli = Cli::parse();

Expand Down Expand Up @@ -191,18 +198,18 @@ pub async fn start(config: Config) -> Result<()> {
}
Cli::Uninstall { version } => {
info!("Starting uninstallation process");
uninstall_handler::start(version.as_deref(), config).await?;
uninstall_handler::start(version.as_deref(), config.config).await?;
}
Cli::Rollback => rollback_handler::start(config).await?,
Cli::Erase => erase_handler::start(config).await?,
Cli::List => list_handler::start(config).await?,
Cli::Rollback => rollback_handler::start(config.config).await?,
Cli::Erase => erase_handler::start(config.config).await?,
Cli::List => list_handler::start(config.config).await?,
Cli::Complete { shell } => {
clap_complete::generate(shell, &mut Cli::command(), "bob", &mut std::io::stdout())
}
Cli::Update(data) => {
update_handler::start(data, &client, config).await?;
}
Cli::ListRemote => list_remote_handler::start(config, client).await?,
Cli::ListRemote => list_remote_handler::start(config.config, client).await?,
}

Ok(())
Expand Down
135 changes: 90 additions & 45 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,84 @@
use anyhow::Result;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::env;
use tokio::fs::{self};
use std::{env, path::PathBuf};
use tokio::{
fs::{self, File},
io::AsyncWriteExt,
};

#[derive(Debug, Clone)]
pub struct ConfigFile {
pub path: PathBuf,
pub format: ConfigFormat,
pub config: Config,
}

impl ConfigFile {
pub async fn save_to_file(&self) -> Result<()> {
if let Some(parent) = self.path.parent() {
tokio::fs::create_dir_all(parent).await?;
}

let data = match self.format {
ConfigFormat::Toml => toml::to_string(&self.config)?,
ConfigFormat::Json => serde_json::to_string_pretty(&self.config)?,
};

let tmp_path = self.path.with_extension("tmp");
let mut file = File::create(&tmp_path).await?;
file.write_all(data.as_bytes()).await?;
file.flush().await?;

// atomic operation i guess
tokio::fs::rename(tmp_path, &self.path).await?;

Ok(())
}
}

impl ConfigFile {
pub async fn get() -> Result<ConfigFile> {
let config_file = crate::helpers::directories::get_config_file()?;
let mut config_format = ConfigFormat::Json;
let config = match fs::read_to_string(&config_file).await {
Ok(config) => {
if config_file.extension().unwrap() == "toml" {
let mut config: Config = toml::from_str(&config)?;
handle_envars(&mut config)?;
config_format = ConfigFormat::Toml;
config
} else {
let mut config: Config = serde_json::from_str(&config)?;
handle_envars(&mut config)?;
config
}
}
Err(_) => Config {
enable_nightly_info: None,
enable_release_build: None,
downloads_location: None,
installation_location: None,
version_sync_file_location: None,
github_mirror: None,
rollback_limit: None,
add_neovim_binary_to_path: None,
},
};

Ok(ConfigFile {
path: config_file,
format: config_format,
config,
})
}
}

#[derive(Debug, Clone)]
pub enum ConfigFormat {
Toml,
Json,
}

/// Represents the application configuration.
///
Expand All @@ -17,6 +93,7 @@ use tokio::fs::{self};
/// * `version_sync_file_location: Option<String>` - The location for the version sync file. This is optional and may be `None`.
/// * `github_mirror: Option<String>` - The GitHub mirror to use. This is optional and may be `None`.
/// * `rollback_limit: Option<u8>` - The rollback limit. This is optional and may be `None`.
/// * `add_neovim_binary_to_path: Option<bool>` - Tells bob whenever to add neovim proxy path to $PATH.
///
/// # Example
///
Expand All @@ -29,60 +106,28 @@ use tokio::fs::{self};
/// version_sync_file_location: Some("/path/to/version_sync_file".to_string()),
/// github_mirror: Some("https://github.com".to_string()),
/// rollback_limit: Some(5),
/// rollback_limit: Some(true),
/// };
/// println!("The configuration is {:?}", config);
/// ```
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Config {
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_nightly_info: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_release_build: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub downloads_location: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub installation_location: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version_sync_file_location: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub github_mirror: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rollback_limit: Option<u8>,
}

/// Handles the application configuration.
///
/// This function reads the configuration file, which can be in either TOML or JSON format, and returns a `Config` object. If the configuration file does not exist, it returns a `Config` object with all fields set to `None`.
///
/// # Returns
///
/// * `Result<Config>` - Returns a `Result` that contains a `Config` object if the function completes successfully. If an error occurs, it returns `Err`.
///
/// # Example
///
/// ```rust
/// let config = handle_config().await.unwrap();
/// println!("The configuration is {:?}", config);
/// ```
pub async fn handle_config() -> Result<Config> {
let config_file = crate::helpers::directories::get_config_file()?;
let config = match fs::read_to_string(&config_file).await {
Ok(config) => {
if config_file.extension().unwrap() == "toml" {
let mut config: Config = toml::from_str(&config)?;
handle_envars(&mut config)?;
config
} else {
let mut config: Config = serde_json::from_str(&config)?;
handle_envars(&mut config)?;
config
}
}
Err(_) => Config {
enable_nightly_info: None,
enable_release_build: None,
downloads_location: None,
installation_location: None,
version_sync_file_location: None,
github_mirror: None,
rollback_limit: None,
},
};

Ok(config)
#[serde(skip_serializing_if = "Option::is_none")]
pub add_neovim_binary_to_path: Option<bool>,
}

/// Handles environment variables in the configuration.
Expand Down
27 changes: 26 additions & 1 deletion src/handlers/erase_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use anyhow::{anyhow, Result};
use tokio::fs;
use tracing::info;

use crate::{config::Config, helpers::directories};
use crate::{
config::Config,
helpers::directories::{self},
};

/// Starts the erase process based on the provided `Config`.
///
Expand Down Expand Up @@ -74,7 +77,29 @@ pub async fn start(config: Config) -> Result<()> {

info!("Successfully removed neovim's installation PATH from registry");
}
} else {
use what_the_path::shell::Shell;
use crate::helpers::directories::get_downloads_directory;

let shell = Shell::detect_by_shell_var()?;

match shell {
Shell::Fish(fish) => {
let files = fish.get_rcfiles()?;
let fish_file = files[0].join("bob.fish");
if !fish_file.exists() { return Ok(()) }
fs::remove_file(fish_file).await?;
},
shell => {
let files = shell.get_rcfiles()?;
let downloads_dir = get_downloads_directory(&config).await?;
let env_path = downloads_dir.join("env/env.sh");
let source_string = format!(". \"{}\"", env_path.display());
for file in files {
what_the_path::shell::remove_from_rcfile(file, &source_string)?;
}
}
}
}
}

Expand Down
27 changes: 14 additions & 13 deletions src/handlers/install_handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::config::Config;
use crate::config::{Config, ConfigFile};
use crate::github_requests::{get_commits_for_nightly, get_upstream_nightly, UpstreamVersion};
use crate::helpers::checksum::sha256cmp;
use crate::helpers::processes::handle_subprocess;
Expand Down Expand Up @@ -64,7 +64,7 @@ use super::{InstallResult, PostDownloadVersionType};
pub async fn start(
version: &mut ParsedVersion,
client: &Client,
config: &Config,
config: &ConfigFile,
) -> Result<InstallResult> {
if version.version_type == VersionType::NightlyRollback {
return Ok(InstallResult::GivenNightlyRollback);
Expand All @@ -76,13 +76,13 @@ pub async fn start(
}
}

let root = directories::get_downloads_directory(config).await?;
let root = directories::get_downloads_directory(&config.config).await?;

env::set_current_dir(&root)?;
let root = root.as_path();

let is_version_installed =
helpers::version::is_version_installed(&version.tag_name, config).await?;
helpers::version::is_version_installed(&version.tag_name, &config.config).await?;

if is_version_installed && version.version_type != VersionType::Nightly {
return Ok(InstallResult::VersionAlreadyInstalled);
Expand All @@ -98,15 +98,15 @@ pub async fn start(
info!("Looking for nightly updates");

let upstream_nightly = nightly_version.as_ref().unwrap();
let local_nightly = helpers::version::nightly::get_local_nightly(config).await?;
let local_nightly = helpers::version::nightly::get_local_nightly(&config.config).await?;

if upstream_nightly.published_at == local_nightly.published_at {
return Ok(InstallResult::NightlyIsUpdated);
}

handle_rollback(config).await?;
handle_rollback(&config.config).await?;

match config.enable_nightly_info {
match config.config.enable_nightly_info {
Some(boolean) if boolean => {
print_commits(client, &local_nightly, upstream_nightly).await?
}
Expand All @@ -117,24 +117,25 @@ pub async fn start(

let downloaded_archive = match version.version_type {
VersionType::Normal | VersionType::Latest => {
download_version(client, version, root, config, false).await
download_version(client, version, root, &config.config, false).await
}
VersionType::Nightly => {
if config.enable_release_build == Some(true) {
handle_building_from_source(version, config).await
if config.config.enable_release_build == Some(true) {
handle_building_from_source(version, &config.config).await
} else {
download_version(client, version, root, config, false).await
download_version(client, version, root, &config.config, false).await
}
}
VersionType::Hash => handle_building_from_source(version, config).await,
VersionType::Hash => handle_building_from_source(version, &config.config).await,
VersionType::NightlyRollback => Ok(PostDownloadVersionType::None),
}?;

if let PostDownloadVersionType::Standard(downloaded_archive) = downloaded_archive {
if version.semver.is_some() && version.semver.as_ref().unwrap() <= &Version::new(0, 4, 4) {
unarchive::start(downloaded_archive).await?
} else {
let downloaded_checksum = download_version(client, version, root, config, true).await?;
let downloaded_checksum =
download_version(client, version, root, &config.config, true).await?;

if let PostDownloadVersionType::Standard(downloaded_checksum) = downloaded_checksum {
let archive_path = root.join(format!(
Expand Down
Loading
Loading