diff --git a/install/src/command.rs b/install/src/command.rs index a253e508f..c7d4b8f8b 100644 --- a/install/src/command.rs +++ b/install/src/command.rs @@ -1,4 +1,4 @@ -use crate::config::Config; +use crate::config::{Config, ExplicitRelease}; use crate::stop_process::stop_process; use crate::update_manifest::{SignedUpdateManifest, UpdateManifest}; use chrono::{Local, TimeZone}; @@ -492,7 +492,7 @@ pub fn init( json_rpc_url: &str, update_manifest_pubkey: &Pubkey, no_modify_path: bool, - release_semver: Option<&str>, + explicit_release: Option, ) -> Result<(), String> { let config = { // Write new config file only if different, so that running |solana-install init| @@ -503,7 +503,7 @@ pub fn init( data_dir, json_rpc_url, update_manifest_pubkey, - release_semver, + explicit_release, ); if current_config != config { config.save(config_file)?; @@ -525,7 +525,7 @@ pub fn init( Ok(()) } -fn github_download_url(release_semver: &str) -> String { +fn github_release_download_url(release_semver: &str) -> String { format!( "https://github.com/solana-labs/solana/releases/download/v{}/solana-release-{}.tar.bz2", release_semver, @@ -533,6 +533,14 @@ fn github_download_url(release_semver: &str) -> String { ) } +fn release_channel_download_url(release_channel: &str) -> String { + format!( + "http://release.solana.com/{}/solana-release-{}.tar.bz2", + release_channel, + crate::build_env::TARGET + ) +} + pub fn info(config_file: &str, local_info_only: bool) -> Result, String> { let config = Config::load(config_file)?; @@ -541,12 +549,24 @@ pub fn info(config_file: &str, local_info_only: bool) -> Result { + println_name_value(&format!("{}Release version:", BULLET), &release_semver); + println_name_value( + &format!("{}Release URL:", BULLET), + &github_release_download_url(release_semver), + ); + } + ExplicitRelease::Channel(release_channel) => { + println_name_value(&format!("{}Release channel:", BULLET), &release_channel); + println_name_value( + &format!("{}Release URL:", BULLET), + &release_channel_download_url(release_channel), + ); + } + } return Ok(None); } @@ -696,13 +716,27 @@ pub fn update(config_file: &str) -> Result { let mut config = Config::load(config_file)?; let update_manifest = info(config_file, false)?; - let release_dir = if let Some(release_semver) = &config.release_semver { - let download_url = github_download_url(release_semver); - let release_dir = config.release_dir(&release_semver); - let ok_dir = release_dir.join(".ok"); - if ok_dir.exists() { - return Ok(false); - } + let release_dir = if let Some(explicit_release) = &config.explicit_release { + let (download_url, release_dir) = match explicit_release { + ExplicitRelease::Semver(release_semver) => { + let download_url = github_release_download_url(release_semver); + let release_dir = config.release_dir(&release_semver); + if release_dir.join(".ok").exists() { + // If this release_semver has already been successfully downloaded, no update + // needed + return Ok(false); + } + (download_url, release_dir) + } + ExplicitRelease::Channel(release_channel) => { + let download_url = release_channel_download_url(release_channel); + let release_dir = config.release_dir(&release_channel); + // Note: There's currently no mechanism to check for an updated binary for a release + // channel so a download always occurs. + (download_url, release_dir) + } + }; + let (_temp_dir, temp_archive, _temp_archive_sha256) = download_to_temp_archive(&download_url, None) .map_err(|err| format!("Unable to download {}: {}", download_url, err))?; @@ -712,7 +746,7 @@ pub fn update(config_file: &str) -> Result { temp_archive, release_dir, err ) })?; - let _ = fs::create_dir_all(ok_dir); + let _ = fs::create_dir_all(release_dir.join(".ok")); release_dir } else { @@ -843,7 +877,7 @@ pub fn run( } }; - if now.elapsed().as_secs() > config.update_poll_secs { + if config.explicit_release.is_none() && now.elapsed().as_secs() > config.update_poll_secs { match update(config_file) { Ok(true) => { // Update successful, kill current process so it will be restart diff --git a/install/src/config.rs b/install/src/config.rs index 5c566e0c2..b7ff17196 100644 --- a/install/src/config.rs +++ b/install/src/config.rs @@ -5,13 +5,19 @@ use std::fs::{create_dir_all, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum ExplicitRelease { + Semver(String), + Channel(String), +} + #[derive(Serialize, Deserialize, Default, Debug, PartialEq)] pub struct Config { pub json_rpc_url: String, pub update_manifest_pubkey: Pubkey, pub current_update_manifest: Option, pub update_poll_secs: u64, - pub release_semver: Option, + pub explicit_release: Option, releases_dir: PathBuf, active_release_dir: PathBuf, } @@ -21,14 +27,14 @@ impl Config { data_dir: &str, json_rpc_url: &str, update_manifest_pubkey: &Pubkey, - release_semver: Option<&str>, + explicit_release: Option, ) -> Self { Self { json_rpc_url: json_rpc_url.to_string(), update_manifest_pubkey: *update_manifest_pubkey, current_update_manifest: None, update_poll_secs: 60, // check for updates once a minute - release_semver: release_semver.map(|s| s.to_string()), + explicit_release, releases_dir: PathBuf::from(data_dir).join("releases"), active_release_dir: PathBuf::from(data_dir).join("active_release"), } diff --git a/install/src/lib.rs b/install/src/lib.rs index 5a014d489..23f13794d 100644 --- a/install/src/lib.rs +++ b/install/src/lib.rs @@ -33,13 +33,20 @@ fn is_pubkey(string: String) -> Result<(), String> { } } -fn is_semver(string: String) -> Result<(), String> { - match semver::Version::parse(&string) { +fn is_semver(semver: &str) -> Result<(), String> { + match semver::Version::parse(&semver) { Ok(_) => Ok(()), Err(err) => Err(format!("{:?}", err)), } } +fn is_release_channel(channel: &str) -> Result<(), String> { + match channel { + "edge" | "beta" | "stable" => Ok(()), + _ => Err(format!("Invalid release channel {}", channel)), + } +} + pub fn main() -> Result<(), String> { solana_logger::setup(); @@ -107,12 +114,12 @@ pub fn main() -> Result<(), String> { } }) .arg( - Arg::with_name("release_semver") - .value_name("release-semver") + Arg::with_name("explicit_release") + .value_name("release") .index(1) .conflicts_with_all(&["json_rpc_url", "update_manifest_pubkey"]) - .validator(is_semver) - .help("The exact version to install. Updates will not be available if this argument is used"), + .validator(|string| is_semver(&string).or_else(|_| is_release_channel(&string))) + .help("The exact version to install. Either a semver or release channel name"), ), ) .subcommand( @@ -206,7 +213,9 @@ pub fn main() -> Result<(), String> { .unwrap(); let data_dir = matches.value_of("data_dir").unwrap(); let no_modify_path = matches.is_present("no_modify_path"); - let release_semver = matches.value_of("release_semver"); + let explicit_release = matches + .value_of("explicit_release") + .map(ToString::to_string); command::init( config_file, @@ -214,7 +223,13 @@ pub fn main() -> Result<(), String> { json_rpc_url, &update_manifest_pubkey, no_modify_path, - release_semver, + explicit_release.map(|explicit_release| { + if is_semver(&explicit_release).is_ok() { + config::ExplicitRelease::Semver(explicit_release) + } else { + config::ExplicitRelease::Channel(explicit_release) + } + }), ) } ("info", Some(matches)) => { @@ -310,11 +325,11 @@ pub fn main_init() -> Result<(), String> { } }) .arg( - Arg::with_name("release_semver") - .value_name("release-semver") + Arg::with_name("explicit_release") + .value_name("release") .index(1) .conflicts_with_all(&["json_rpc_url", "update_manifest_pubkey"]) - .validator(is_semver) + .validator(|string| is_semver(&string).or_else(|_| is_release_channel(&string))) .help("The exact version to install. Updates will not be available if this argument is used"), ) .get_matches(); @@ -329,7 +344,9 @@ pub fn main_init() -> Result<(), String> { .unwrap(); let data_dir = matches.value_of("data_dir").unwrap(); let no_modify_path = matches.is_present("no_modify_path"); - let release_semver = matches.value_of("release_semver"); + let explicit_release = matches + .value_of("explicit_release") + .map(ToString::to_string); command::init( config_file, @@ -337,6 +354,12 @@ pub fn main_init() -> Result<(), String> { json_rpc_url, &update_manifest_pubkey, no_modify_path, - release_semver, + explicit_release.map(|explicit_release| { + if is_semver(&explicit_release).is_ok() { + config::ExplicitRelease::Semver(explicit_release) + } else { + config::ExplicitRelease::Channel(explicit_release) + } + }), ) }