Expand Up
@@ -13,15 +13,17 @@ mod s3;
use clap:: { Parser , Subcommand } ;
use color_eyre:: { eyre:: eyre, Result } ;
use github:: GithubReleaseRepository ;
use install:: AssetType ;
use install:: { AssetType , Settings } ;
use s3:: S3AssetRepository ;
use std:: env:: consts:: { ARCH , OS } ;
use std:: path:: PathBuf ;
use std:: path:: { Path , PathBuf } ;
const GITHUB_API_URL : & str = "https://api.github.com" ;
const ORG_NAME : & str = "maidsafe" ;
const REPO_NAME : & str = "safe_network" ;
const SAFE_BUCKET_NAME : & str = "https://sn-cli.s3.eu-west-2.amazonaws.com" ;
const SAFENODE_BUCKET_NAME : & str = "https://sn-node.s3.eu-west-2.amazonaws.com" ;
const TESTNET_BUCKET_NAME : & str = "https://sn-testnet.s3.eu-west-2.amazonaws.com" ;
#[ derive( Parser ) ]
#[ command( version, about) ]
Expand All
@@ -36,85 +38,205 @@ enum Commands {
///
/// The default install path is /usr/local/bin if you run safeup as root, or ~/.safe/cli if you
/// run as the current user.
///
/// If running as the current user, the shell profile will be modified to put safe on PATH.
Client {
/// Override the default installation path.
///
/// Any directories that don't exist will be created.
#[ arg( short, long, value_name = "DIRECTORY" ) ]
#[ arg( short = 'p' , long, value_name = "DIRECTORY" ) ]
path : Option < PathBuf > ,
/// Disable modification of the shell profile.
#[ arg( short = 'n' , long) ]
no_modify_shell_profile : bool ,
/// Install a specific version rather than the latest.
#[ arg( short = 'v' , long) ]
version : Option < String > ,
} ,
/// Install the latest version of safenode.
///
/// The default install path is /usr/local/bin if you run safeup as root, or ~/.safe/node if you
/// run as the current user.
///
/// If running as the current user, the shell profile will be modified to put safe on PATH.
Node {
/// Override the default installation path.
///
/// Any directories that don't exist will be created.
#[ arg( short = 'p' , long, value_name = "DIRECTORY" ) ]
path : Option < PathBuf > ,
/// Disable modification of the shell profile.
#[ arg( short = 'n' , long) ]
no_modify_shell_profile : bool ,
/// Install a specific version rather than the latest.
#[ arg( short = 'v' , long) ]
version : Option < String > ,
} ,
/// Install the latest version of testnet.
///
/// The default install path is /usr/local/bin if you run safeup as root, or ~/.safe/node if you
/// run as the current user.
///
/// If running as the current user, the shell profile will be modified to put safe on PATH.
Testnet {
/// Override the default installation path.
///
/// Any directories that don't exist will be created.
#[ arg( short = 'p' , long, value_name = "DIRECTORY" ) ]
path : Option < PathBuf > ,
/// Disable modification of the shell profile.
#[ arg( short = 'n' , long) ]
no_modify_shell_profile : bool ,
/// Install a specific version rather than the latest.
#[ arg( short = 'v' , long) ]
version : Option < String > ,
} ,
}
#[ tokio:: main]
async fn main ( ) -> Result < ( ) > {
let platform = get_platform ( ) ?;
let cli = Cli :: parse ( ) ;
let result = match cli. command {
Some ( Commands :: Client { path } ) => {
let dest_dir_path = if let Some ( path) = path {
path
} else {
if is_running_elevated ( ) {
std:: path:: PathBuf :: from ( "/usr/local/bin" )
} else {
let home_dir_path = dirs_next:: home_dir ( )
. ok_or_else ( || eyre ! ( "Could not retrieve user's home directory" ) ) ?;
home_dir_path. join ( ".safe" ) . join ( "cli" )
}
} ;
let release_repository =
GithubReleaseRepository :: new ( GITHUB_API_URL , ORG_NAME , REPO_NAME ) ;
let asset_repository = S3AssetRepository :: new ( SAFE_BUCKET_NAME ) ;
install:: install_bin (
match cli. command {
Some ( Commands :: Client {
path,
no_modify_shell_profile,
version,
} ) => {
install (
AssetType :: Client ,
release_repository ,
asset_repository ,
& platform ,
dest_dir_path . clone ( ) ,
SAFE_BUCKET_NAME ,
path ,
version ,
no_modify_shell_profile ,
)
. await ?;
Ok ( ( ) )
. await
}
Some ( Commands :: Node {
path,
no_modify_shell_profile,
version,
} ) => {
install (
AssetType :: Node ,
SAFENODE_BUCKET_NAME ,
path,
version,
no_modify_shell_profile,
)
. await
}
Some ( Commands :: Testnet {
path,
no_modify_shell_profile,
version,
} ) => {
install (
AssetType :: Testnet ,
TESTNET_BUCKET_NAME ,
path,
version,
no_modify_shell_profile,
)
. await
}
None => {
println ! ( "interactive gui" ) ;
Ok ( ( ) )
}
}
}
async fn install (
asset_type : AssetType ,
bucket_name : & str ,
path : Option < PathBuf > ,
version : Option < String > ,
no_modify_shell_profile : bool ,
) -> Result < ( ) > {
let platform = get_platform ( ) ?;
let running_elevated = is_running_elevated ( ) ;
let home_dir_path =
dirs_next:: home_dir ( ) . ok_or_else ( || eyre ! ( "Could not retrieve user's home directory" ) ) ?;
let safe_dir_path = home_dir_path. join ( ".safe" ) ;
let dest_dir_path = if let Some ( path) = path {
path
} else if running_elevated {
std:: path:: PathBuf :: from ( "/usr/local/bin" )
} else {
let dir = match asset_type {
AssetType :: Client => "cli" ,
AssetType :: Node => "node" ,
AssetType :: Testnet => "node" ,
} ;
safe_dir_path. join ( dir)
} ;
result
let release_repository = GithubReleaseRepository :: new ( GITHUB_API_URL , ORG_NAME , REPO_NAME ) ;
let asset_repository = S3AssetRepository :: new ( bucket_name) ;
let ( _, bin_path) = install:: install_bin (
asset_type. clone ( ) ,
release_repository,
asset_repository,
& platform,
dest_dir_path. clone ( ) ,
version,
)
. await ?;
if !running_elevated && !no_modify_shell_profile {
install:: configure_shell_profile (
& get_shell_profile_path ( & home_dir_path) ,
& home_dir_path. join ( ".safe" ) . join ( "env" ) ,
)
. await ?
}
let settings_file_path = safe_dir_path. join ( "safeup.json" ) ;
let mut settings = Settings :: read ( & settings_file_path) ?;
match asset_type {
AssetType :: Client => settings. safe_path = bin_path,
AssetType :: Node => settings. safenode_path = bin_path,
AssetType :: Testnet => settings. testnet_path = bin_path,
}
settings. save ( & settings_file_path) ?;
Ok ( ( ) )
}
fn get_platform ( ) -> Result < String > {
match OS {
"linux" => match ARCH {
"x86_64" => return Ok ( format ! ( "{}-unknown-{}-musl" , ARCH , OS ) ) ,
"armv7" => return Ok ( format ! ( "{}-unknown-{}-musleabihf" , ARCH , OS ) ) ,
"arm" => return Ok ( format ! ( "{}-unknown-{}-musleabi" , ARCH , OS ) ) ,
"aarch64" => return Ok ( format ! ( "{}-unknown-{}-musl" , ARCH , OS ) ) ,
& _ => {
return Err ( eyre ! (
"We currently do not have binaries for the {OS}/{ARCH} combination"
) )
}
"x86_64" => Ok ( format ! ( "{}-unknown-{}-musl" , ARCH , OS ) ) ,
"armv7" => Ok ( format ! ( "{}-unknown-{}-musleabihf" , ARCH , OS ) ) ,
"arm" => Ok ( format ! ( "{}-unknown-{}-musleabi" , ARCH , OS ) ) ,
"aarch64" => Ok ( format ! ( "{}-unknown-{}-musl" , ARCH , OS ) ) ,
& _ => Err ( eyre ! (
"We currently do not have binaries for the {OS}/{ARCH} combination"
) ) ,
} ,
"windows" => {
if ARCH != "x86_64" {
return Err ( eyre ! (
"We currently only have x86_64 binaries available for Windows"
) ) ;
}
return Ok ( format ! ( "{}-pc-{}-msvc" , ARCH , OS ) ) ;
Ok ( format ! ( "{}-pc-{}-msvc" , ARCH , OS ) )
}
"macos" => {
if ARCH != "x86_64" {
return Err ( eyre ! (
"We currently only have x86_64 binaries available for macOS"
) ) ;
}
return Ok ( format ! ( "{}-apple-darwin" , ARCH ) ) ;
}
& _ => {
return Err ( eyre ! ( "{OS} is not currently supported by safeup" ) ) ;
Ok ( format ! ( "{}-apple-darwin" , ARCH ) )
}
& _ => Err ( eyre ! ( "{OS} is not currently supported by safeup" ) ) ,
}
}
Expand All
@@ -134,3 +256,20 @@ fn is_running_elevated() -> bool {
fn is_running_elevated ( ) -> bool {
false
}
#[ cfg( target_os = "linux" ) ]
fn get_shell_profile_path ( home_dir_path : & Path ) -> PathBuf {
home_dir_path. join ( ".bashrc" )
}
/// We won't actually end up doing anything on Windows with the shell profile, so we can just
/// return back the home directory.
#[ cfg( target_os = "windows" ) ]
fn get_shell_profile_path ( home_dir_path : & Path ) -> PathBuf {
home_dir_path. to_path_buf ( )
}
#[ cfg( target_os = "macos" ) ]
fn get_shell_profile_path ( home_dir_path : & Path ) -> PathBuf {
home_dir_path. join ( ".zshrc" )
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a small suggestion, but can we maybe print the help screen if no args were passed? https://docs.rs/clap/latest/clap/struct.Command.html#method.print_help
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Roland, thanks for the feedback. My plan was that if no arguments were passed, we would run an interactive GUI, which is what
rustupdoes too. Well, sorry, just to clarify, it's not a GUI, I guess, but a TUI.Do you think that's not worth doing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see! That would be pretty useful to the non-tech people I assume. But I'm not so sure, they will again be exposed to the techy CLIs of
safenode/safe/testnetafter using this tool.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, to be honest, the value of it may be quite low. I might ask this question on the forum.