Skip to content

Commit 5e1e28a

Browse files
abueideclaude
andcommitted
feat(segkit): add setup subcommand to auto-install devbox
Adds `segkit setup` which checks if devbox is installed and automatically runs Jetify's install script (https://get.jetify.com/devbox) if not found. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fb83ba8 commit 5e1e28a

3 files changed

Lines changed: 69 additions & 1 deletion

File tree

segkit/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::process::ExitCode;
33
use clap::{Parser, Subcommand};
44

55
mod delegate;
6+
mod setup;
67

78
#[derive(Parser)]
89
#[command(name = "segkit", version, about = "Segment SDK developer toolkit")]
@@ -28,6 +29,8 @@ enum Commands {
2829
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
2930
args: Vec<String>,
3031
},
32+
/// Check and install required dependencies (devbox)
33+
Setup,
3134
}
3235

3336
fn main() -> ExitCode {
@@ -37,6 +40,7 @@ fn main() -> ExitCode {
3740
Some(Commands::Android { args }) => delegate::run("android.sh", &args),
3841
Some(Commands::Ios { args }) => delegate::run("ios.sh", &args),
3942
Some(Commands::Rn { args }) => delegate::run("rn.sh", &args),
43+
Some(Commands::Setup) => setup::run(),
4044
None => {
4145
println!("segkit {}", env!("CARGO_PKG_VERSION"));
4246
ExitCode::SUCCESS

segkit/src/setup.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::process::{Command, ExitCode};
2+
3+
const DEVBOX_INSTALL_URL: &str = "https://get.jetify.com/devbox";
4+
5+
fn is_installed(cmd: &str) -> bool {
6+
which::which(cmd).is_ok()
7+
}
8+
9+
fn install_devbox() -> Result<(), String> {
10+
eprintln!("segkit: devbox not found, installing via {DEVBOX_INSTALL_URL}");
11+
12+
let curl_available = is_installed("curl");
13+
let wget_available = is_installed("wget");
14+
15+
let status = if curl_available {
16+
Command::new("sh")
17+
.args(["-c", &format!("curl -fsSL {DEVBOX_INSTALL_URL} | bash")])
18+
.status()
19+
} else if wget_available {
20+
Command::new("sh")
21+
.args(["-c", &format!("wget -qO- {DEVBOX_INSTALL_URL} | bash")])
22+
.status()
23+
} else {
24+
return Err("neither curl nor wget found — cannot download devbox installer".into());
25+
};
26+
27+
match status {
28+
Ok(s) if s.success() => Ok(()),
29+
Ok(s) => Err(format!(
30+
"devbox installer exited with code {}",
31+
s.code().unwrap_or(-1)
32+
)),
33+
Err(e) => Err(format!("failed to run installer: {e}")),
34+
}
35+
}
36+
37+
pub fn run() -> ExitCode {
38+
if is_installed("devbox") {
39+
println!("devbox: installed");
40+
} else {
41+
if let Err(e) = install_devbox() {
42+
eprintln!("segkit: {e}");
43+
return ExitCode::FAILURE;
44+
}
45+
if !is_installed("devbox") {
46+
eprintln!(
47+
"segkit: devbox installed but not found on PATH — you may need to restart your shell"
48+
);
49+
return ExitCode::FAILURE;
50+
}
51+
println!("devbox: installed");
52+
}
53+
54+
ExitCode::SUCCESS
55+
}

segkit/tests/cli.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ fn help_flag() {
2222
.stdout(predicate::str::contains("Segment SDK developer toolkit"))
2323
.stdout(predicate::str::contains("android"))
2424
.stdout(predicate::str::contains("ios"))
25-
.stdout(predicate::str::contains("rn"));
25+
.stdout(predicate::str::contains("rn"))
26+
.stdout(predicate::str::contains("setup"));
2627
}
2728

2829
#[test]
@@ -66,3 +67,11 @@ fn rn_subcommand_without_script_fails_gracefully() {
6667
.failure()
6768
.stderr(predicate::str::contains("rn.sh not found"));
6869
}
70+
71+
#[test]
72+
fn setup_detects_devbox() {
73+
segkit()
74+
.arg("setup")
75+
.assert()
76+
.stdout(predicate::str::contains("devbox:"));
77+
}

0 commit comments

Comments
 (0)