diff --git a/Cargo.lock b/Cargo.lock index da52e9b5f..ca0f20449 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,7 @@ dependencies = [ "rpassword 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 3fe923db7..2f0d0789c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ ethminer = { path = "miner" } ethcore-devtools = { path = "devtools" } ethcore-rpc = { path = "rpc", optional = true } ethcore-webapp = { path = "webapp", optional = true } - +semver = "0.2" [dependencies.hyper] version = "0.8" diff --git a/parity/main.rs b/parity/main.rs index e2e4c9c37..87e92cde3 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -36,6 +36,7 @@ extern crate daemonize; extern crate time; extern crate number_prefix; extern crate rpassword; +extern crate semver; // for price_info.rs #[macro_use] extern crate hyper; @@ -71,6 +72,7 @@ use rpc::Server as RpcServer; use webapp::Listening as WebappServer; mod price_info; +mod upgrade; fn die_with_message(msg: &str) -> ! { println!("ERROR: {}", msg); @@ -785,6 +787,17 @@ fn die_with_io_error(e: std::io::Error) -> ! { } fn main() { + match ::upgrade::upgrade() { + Ok(upgrades_applied) => { + if upgrades_applied > 0 { + println!("Executed {} upgrade scripts - ok", upgrades_applied); + } + }, + Err(e) => { + die!("Error upgrading parity data: {:?}", e); + } + } + Configuration::parse().execute(); } diff --git a/parity/upgrade.rs b/parity/upgrade.rs new file mode 100644 index 000000000..c9c98e174 --- /dev/null +++ b/parity/upgrade.rs @@ -0,0 +1,126 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Parity upgrade logic + +use semver::Version; +use std::collections::*; +use std::fs::File; +use std::env; +use std::io::{Read, Write}; + +#[derive(Debug)] +pub enum Error { + CannotLockVersionFile, + CannotUpdateVersionFile, +} + +const CURRENT_VERSION: &'static str = env!("CARGO_PKG_VERSION"); + +#[derive(Hash, PartialEq, Eq)] +struct UpgradeKey { + pub old_version: Version, + pub new_version: Version, +} + +type UpgradeList = HashMap Result<(), Error>>; + +impl UpgradeKey { + // given the following config exist + // ver.lock 1.1 (`previous_version`) + // + // current_version 1.4 (`current_version`) + // + // + //upgrades (set of `UpgradeKey`) + // 1.0 -> 1.1 (u1) + // 1.1 -> 1.2 (u2) + // 1.2 -> 1.3 (u3) + // 1.3 -> 1.4 (u4) + // 1.4 -> 1.5 (u5) + // + // then the following upgrades should be applied: + // u2, u3, u4 + fn is_applicable(&self, previous_version: &Version, current_version: &Version) -> bool { + self.old_version >= *previous_version && self.new_version <= *current_version + } +} + +// dummy upgrade (remove when the first one is in) +fn dummy_upgrade() -> Result<(), Error> { + println!("Adding ver.lock"); + Ok(()) +} + +fn push_updrades(upgrades: &mut UpgradeList) +{ + // dummy upgrade (remove when the first one is in) + upgrades.insert( + UpgradeKey { old_version: Version::parse("0.9.0").unwrap(), new_version: Version::parse("1.0.0").unwrap() }, + dummy_upgrade); +} + +fn upgrade_from_version(previous_version: &Version) -> Result { + let mut upgrades = HashMap::new(); + push_updrades(&mut upgrades); + + let current_version = Version::parse(CURRENT_VERSION).unwrap(); + + let mut count = 0; + for upgrade_key in upgrades.keys() { + if upgrade_key.is_applicable(previous_version, ¤t_version) { + let upgrade_script = upgrades[upgrade_key]; + try!(upgrade_script()); + count = count + 1; + } + } + Ok(count) +} + +fn with_locked_version(script: F) -> Result + where F: Fn(&Version) -> Result +{ + let mut path = env::home_dir().expect("Applications should have a home dir"); + path.push(".parity"); + path.push("ver.lock"); + + let version = + File::open(&path).ok().and_then(|ref mut file| + { + let mut version_string = String::new(); + file.read_to_string(&mut version_string) + .ok() + .and_then(|_| Version::parse(&version_string).ok()) + }) + .unwrap_or_else(|| Version::parse("0.9.0").unwrap()); + + let script_result = { + let mut lock = try!(File::create(&path).map_err(|_| Error::CannotLockVersionFile)); + let result = script(&version); + + let written_version = Version::parse(CURRENT_VERSION).unwrap(); + try!(lock.write_all(written_version.to_string().as_bytes()).map_err(|_| Error::CannotUpdateVersionFile)); + result + }; + + script_result +} + +pub fn upgrade() -> Result { + with_locked_version(|ver| { + upgrade_from_version(ver) + }) +}