Skip to content

Commit 1c19625

Browse files
committed
Init once.
1 parent e90b241 commit 1c19625

File tree

20 files changed

+507
-945
lines changed

20 files changed

+507
-945
lines changed

crates/rb-cli/src/bin/rb.rs

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,11 @@ fn main() {
5353

5454
let cli = Cli::parse();
5555

56-
if let Some(Commands::BashComplete { line, point }) = &cli.command {
57-
rb_cli::completion::generate_completions(line, point, cli.config.rubies_dir.clone());
58-
return;
56+
// Skip logging for bash completion (must be silent)
57+
if !matches!(cli.command, Some(Commands::BashComplete { .. })) {
58+
init_logger(cli.effective_log_level());
5959
}
6060

61-
init_logger(cli.effective_log_level());
62-
6361
// Merge config file defaults with CLI arguments
6462
let cli = match cli.with_config_defaults() {
6563
Ok(cli) => cli,
@@ -101,54 +99,72 @@ fn main() {
10199
return;
102100
}
103101

104-
// Handle sync command differently since it doesn't use ButlerRuntime in the same way
105-
if let Commands::Sync = command {
106-
if let Err(e) = sync_command(
107-
cli.config.rubies_dir.clone(),
108-
cli.config.ruby_version.clone(),
109-
cli.config.gem_home.clone(),
110-
cli.config.no_bundler.unwrap_or(false),
111-
) {
112-
eprintln!("Sync failed: {}", e);
113-
std::process::exit(1);
114-
}
115-
return;
116-
}
117-
118102
// Resolve search directory for Ruby installations
119-
let rubies_dir = resolve_search_dir(cli.config.rubies_dir);
103+
let rubies_dir = resolve_search_dir(cli.config.rubies_dir.clone());
120104

121105
// Perform comprehensive environment discovery once
106+
let is_completion = matches!(command, Commands::BashComplete { .. });
107+
122108
let butler_runtime = match ButlerRuntime::discover_and_compose_with_gem_base(
123-
rubies_dir,
124-
cli.config.ruby_version,
125-
cli.config.gem_home,
109+
rubies_dir.clone(),
110+
cli.config.ruby_version.clone(),
111+
cli.config.gem_home.clone(),
126112
cli.config.no_bundler.unwrap_or(false),
127113
) {
128114
Ok(runtime) => runtime,
129-
Err(e) => match e {
130-
ButlerError::RubiesDirectoryNotFound(path) => {
131-
eprintln!("🎩 My sincerest apologies, but the designated Ruby estate directory");
132-
eprintln!(
133-
" '{}' appears to be absent from your system.",
134-
path.display()
135-
);
136-
eprintln!();
137-
eprintln!("Without access to a properly established Ruby estate, I'm afraid");
138-
eprintln!(
139-
"there's precious little this humble Butler can accomplish on your behalf."
140-
);
141-
eprintln!();
142-
eprintln!("May I suggest installing Ruby using ruby-install or a similar");
143-
eprintln!("distinguished tool to establish your Ruby installations at the");
144-
eprintln!("expected location, then we shall proceed with appropriate ceremony.");
145-
std::process::exit(1);
146-
}
147-
_ => {
148-
eprintln!("Error: {}", e);
149-
std::process::exit(1);
115+
Err(e) => {
116+
if is_completion {
117+
// Completion: exit silently with no suggestions when no Ruby found
118+
std::process::exit(0);
119+
} else {
120+
// Interactive commands: show helpful error with search details
121+
match e {
122+
ButlerError::RubiesDirectoryNotFound(path) => {
123+
eprintln!(
124+
"The designated Ruby estate directory appears to be absent from your system."
125+
);
126+
eprintln!();
127+
eprintln!("Searched in:");
128+
eprintln!(" • {}", path.display());
129+
130+
// Show why this path was used
131+
if let Some(ref config_rubies) = cli.config.rubies_dir {
132+
eprintln!(" (from config: {})", config_rubies.display());
133+
} else {
134+
eprintln!(" (default location)");
135+
}
136+
137+
if let Some(ref requested_version) = cli.config.ruby_version {
138+
eprintln!();
139+
eprintln!("Requested version: {}", requested_version);
140+
}
141+
142+
eprintln!();
143+
eprintln!(
144+
"May I suggest installing Ruby using ruby-install or a similar distinguished tool?"
145+
);
146+
std::process::exit(1);
147+
}
148+
ButlerError::NoSuitableRuby(msg) => {
149+
eprintln!("No suitable Ruby installation found: {}", msg);
150+
eprintln!();
151+
eprintln!("Searched in: {}", rubies_dir.display());
152+
153+
if let Some(ref requested_version) = cli.config.ruby_version {
154+
eprintln!("Requested version: {}", requested_version);
155+
}
156+
157+
eprintln!();
158+
eprintln!("May I suggest installing a suitable Ruby version?");
159+
std::process::exit(1);
160+
}
161+
_ => {
162+
eprintln!("Ruby detection encountered an unexpected difficulty: {}", e);
163+
std::process::exit(1);
164+
}
165+
}
150166
}
151-
},
167+
}
152168
};
153169

154170
match command {
@@ -169,10 +185,14 @@ fn main() {
169185
unreachable!()
170186
}
171187
Commands::Sync => {
172-
// Already handled above
173-
unreachable!()
188+
if let Err(e) = sync_command(butler_runtime) {
189+
eprintln!("Sync failed: {}", e);
190+
std::process::exit(1);
191+
}
174192
}
175193
Commands::ShellIntegration { .. } => unreachable!(),
176-
Commands::BashComplete { .. } => unreachable!(),
194+
Commands::BashComplete { line, point } => {
195+
rb_cli::completion::generate_completions(&line, &point, &butler_runtime);
196+
}
177197
}
178198
}

crates/rb-cli/src/commands/sync.rs

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,11 @@ use log::debug;
22
use rb_core::bundler::SyncResult;
33
use rb_core::butler::ButlerRuntime;
44

5-
pub fn sync_command(
6-
rubies_dir: Option<std::path::PathBuf>,
7-
requested_ruby_version: Option<String>,
8-
gem_home: Option<std::path::PathBuf>,
9-
no_bundler: bool,
10-
) -> Result<(), Box<dyn std::error::Error>> {
5+
pub fn sync_command(butler_runtime: ButlerRuntime) -> Result<(), Box<dyn std::error::Error>> {
116
debug!("Starting sync command");
127

13-
// Check if --no-bundler flag is set
14-
if no_bundler {
15-
eprintln!("❌ Sync Requires Bundler Environment");
16-
eprintln!();
17-
eprintln!("The sync command cannot operate without bundler, as it is designed");
18-
eprintln!("to synchronize bundler-managed gem dependencies.");
19-
eprintln!();
20-
eprintln!("Please remove the --no-bundler (-B) flag to use sync:");
21-
eprintln!(" rb sync");
22-
eprintln!();
23-
std::process::exit(1);
24-
}
25-
26-
// Resolve search directory
27-
let search_dir = crate::resolve_search_dir(rubies_dir);
28-
29-
// Discover and compose the butler runtime with optional custom gem base
30-
// Note: sync command always needs bundler, so skip_bundler is always false
31-
let runtime = ButlerRuntime::discover_and_compose_with_gem_base(
32-
search_dir,
33-
requested_ruby_version,
34-
gem_home,
35-
false,
36-
)?;
37-
388
// Check if bundler runtime is available
39-
let bundler_runtime = match runtime.bundler_runtime() {
9+
let bundler_runtime = match butler_runtime.bundler_runtime() {
4010
Some(bundler) => bundler,
4111
None => {
4212
println!("⚠️ Bundler Environment Not Detected");
@@ -47,6 +17,9 @@ pub fn sync_command(
4717
println!("To create a new bundler project:");
4818
println!(" • Create a Gemfile with: echo 'source \"https://rubygems.org\"' > Gemfile");
4919
println!(" • Then run: rb sync");
20+
println!();
21+
println!("💡 Note: If you used the --no-bundler (-B) flag, please remove it:");
22+
println!(" rb sync");
5023
return Err("No bundler environment detected".into());
5124
}
5225
};
@@ -59,7 +32,7 @@ pub fn sync_command(
5932
println!();
6033

6134
// Perform synchronization
62-
match bundler_runtime.synchronize(&runtime, |line| {
35+
match bundler_runtime.synchronize(&butler_runtime, |line| {
6336
println!("{}", line);
6437
}) {
6538
Ok(SyncResult::AlreadySynced) => {
@@ -157,35 +130,36 @@ mod tests {
157130
fn test_sync_command_with_no_gemfile() -> Result<(), Box<dyn std::error::Error>> {
158131
let sandbox = BundlerSandbox::new()?;
159132
let project_dir = sandbox.add_dir("no_gemfile_project")?;
160-
161-
// Create a temporary rubies directory to avoid CI failure
162133
let rubies_dir = sandbox.add_dir("rubies")?;
163134

164135
// Change to project directory
165136
let original_dir = std::env::current_dir()?;
166137
std::env::set_current_dir(&project_dir)?;
167138

168-
// Should return error when no bundler environment detected
169-
let result = sync_command(Some(rubies_dir), None, None, false);
139+
// Try to create a ButlerRuntime without bundler (no Gemfile)
140+
let result = ButlerRuntime::discover_and_compose_with_gem_base(
141+
rubies_dir.clone(),
142+
None,
143+
None,
144+
false,
145+
);
170146

171-
// Restore directory (ignore errors in case directory was deleted)
147+
// Restore directory
172148
let _ = std::env::set_current_dir(original_dir);
173149

174-
// Should return error when no bundler environment detected
175150
match result {
176-
Ok(()) => panic!("Expected error when no Gemfile found, but command succeeded"),
177-
Err(e) => {
178-
let error_msg = e.to_string();
179-
if error_msg.contains("No bundler environment detected")
180-
|| error_msg.contains("Os { code: 2")
181-
|| error_msg.contains("No such file or directory")
182-
|| error_msg.contains("Bundler executable not found")
183-
|| error_msg.contains("No suitable Ruby installation found")
184-
{
185-
Ok(()) // Expected errors in test environment without bundler/ruby
186-
} else {
187-
Err(e) // Unexpected error
188-
}
151+
Ok(runtime) => {
152+
// If runtime creation succeeded (found Ruby), sync should fail due to no Gemfile
153+
let sync_result = sync_command(runtime);
154+
assert!(
155+
sync_result.is_err(),
156+
"Expected sync to fail without Gemfile"
157+
);
158+
Ok(())
159+
}
160+
Err(_) => {
161+
// Expected in test environment without Ruby installation
162+
Ok(())
189163
}
190164
}
191165
}

0 commit comments

Comments
 (0)