Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: provide node-manager command
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the new settings file will have additional entries for keeping track of the
safenode-manager installation. Previously serialised settings files will be incompatible with this
change. Users will need to clear their previous settings file when they upgrade.

This will install the node manager alongside the other binaries. After the `sn-releases` crate was
extended for the new release type, adding the new command was quite straight forward, since it's
just the installation of another binary.

At some point later we may need to extend this installation to also distribute the RPC client, which
may be required for the node manager if we're going to use it in service shutdown commands. For now
though, it's just distributing the `safenode-manager` binary.
jacderida committed Dec 14, 2023
1 parent 58aa96f commit d915e7e
Showing 6 changed files with 190 additions and 12 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ env:
RUSTFLAGS: "-D warnings"
CLIENT_VERSION: 0.77.27
NODE_VERSION: 0.83.25
NODE_MANAGER_VERSION: 0.1.8
TESTNET_VERSION: 0.1.29

jobs:
@@ -140,6 +141,7 @@ jobs:
run: |
cargo run -- client --version $env:CLIENT_VERSION
cargo run -- node --version $env:NODE_VERSION
cargo run -- node-manager --version $env:NODE_MANAGER_VERSION
cargo run -- testnet --version $env:TESTNET_VERSION
- name: Check if binaries are available in new shell session
shell: pwsh
@@ -152,6 +154,10 @@ jobs:
Write-Host "safenode.exe does not exist"
exit 1
}
if (!(Test-Path "$env:USERPROFILE\safe\safenode-manager.exe")) {
Write-Host "safenode-manager.exe does not exist"
exit 1
}
if (!(Test-Path "$env:USERPROFILE\safe\testnet.exe")) {
Write-Host "testnet.exe does not exist"
exit 1
@@ -185,6 +191,18 @@ jobs:
exit 1
}
$output = & "${env:USERPROFILE}\safe\safenode-manager.exe" --version
$version = $output | Select-String -Pattern "sn-node-manager (\d+\.\d+\.\d+)"
$versionNumber = $version.Matches.Groups[1].Value
if ($versionNumber -eq "$env:NODE_MANAGER_VERSION") {
Write-Host "The correct version of safenode-manager has been installed"
} else {
Write-Host "The correct version of safenode-manager has not been installed"
Write-Host "We expected version $env:NODE_MANAGER_VERSION"
Write-Host "The downloaded binary has $versionNumber"
exit 1
}
$output = & "${env:USERPROFILE}\safe\testnet.exe" --version
$version = $output | Select-String -Pattern "testnet (\d+\.\d+\.\d+)"
$versionNumber = $version.Matches.Groups[1].Value
@@ -213,6 +231,7 @@ jobs:
run: |
cargo run -- client --version $CLIENT_VERSION
cargo run -- node --version $NODE_VERSION
cargo run -- node-manager --version $NODE_MANAGER_VERSION
cargo run -- testnet --version $TESTNET_VERSION
- name: Check if binaries are available in new shell session
shell: bash
@@ -228,6 +247,7 @@ jobs:
[[ -f "$HOME/.local/bin/safe" ]] || { echo "safe not in expected location"; exit 1; }
[[ -f "$HOME/.local/bin/safenode" ]] || { echo "safenode not in expected location"; exit 1; }
[[ -f "$HOME/.local/bin/safenode-manager" ]] || { echo "safenode-manager not in expected location"; exit 1; }
[[ -f "$HOME/.local/bin/testnet" ]] || { echo "testnet not in expected location"; exit 1; }
version=$(safe --version | awk '{ print $2 }')
@@ -250,6 +270,16 @@ jobs:
exit 1
fi
version=$(safenode-manager --version | awk '{ print $2 }')
if [[ "$version" == "$NODE_MANAGER_VERSION" ]]; then
echo "The correct version of safenode-manager has been installed"
else
echo "The correct version of safenode-manager has not been installed"
echo "We expected $NODE_MANAGER_VERSION"
echo "The downloaded binary has $version"
exit 1
fi
version=$(testnet --version | awk '{ print $2 }')
if [[ "$version" == "$TESTNET_VERSION" ]]; then
echo "The correct version of testnet has been installed"
@@ -275,6 +305,7 @@ jobs:
run: |
cargo run -- client --version $CLIENT_VERSION
cargo run -- node --version $NODE_VERSION
cargo run -- node-manager --version $NODE_MANAGER_VERSION
cargo run -- testnet --version $TESTNET_VERSION
- name: Check if binaries are available in new shell session
run: |
@@ -283,6 +314,7 @@ jobs:
[[ -f "$HOME/.local/bin/safe" ]] || { echo "safe not in expected location"; exit 1; }
[[ -f "$HOME/.local/bin/safenode" ]] || { echo "safenode not in expected location"; exit 1; }
[[ -f "$HOME/.local/bin/safenode-manager" ]] || { echo "safenode-manager not in expected location"; exit 1; }
[[ -f "$HOME/.local/bin/testnet" ]] || { echo "testnet not in expected location"; exit 1; }
version=$(safe --version | awk '{ print $2 }')
@@ -305,6 +337,16 @@ jobs:
exit 1
fi
version=$(safenode-manager --version | awk '{ print $2 }')
if [[ "$version" == "$NODE_MANAGER_VERSION" ]]; then
echo "The correct version of safenode-manager has been installed"
else
echo "The correct version of safenode-manager has not been installed"
echo "We expected $NODE_MANAGER_VERSION"
echo "The downloaded binary has $version"
exit 1
fi
version=$(testnet --version | awk '{ print $2 }')
if [[ "$version" == "$TESTNET_VERSION" ]]; then
echo "The correct version of testnet has been installed"
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ semver = "1.0.4"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
sn-releases = { git = "https://github.com/jacderida/sn-releases", branch = "custom-url" }
sn-releases = "0.1.5"
tempfile = "3.8.1"
textwrap = "0.16.0"
tokio = { version = "1.26", features = ["full"] }
4 changes: 4 additions & 0 deletions src/cmd.rs
Original file line number Diff line number Diff line change
@@ -172,6 +172,10 @@ async fn do_install_binary(
settings.safenode_path = bin_path;
settings.safenode_version = installed_version;
}
AssetType::NodeManager => {
settings.safenode_manager_path = bin_path;
settings.safenode_manager_version = installed_version;
}
AssetType::Testnet => {
settings.testnet_path = bin_path;
settings.testnet_version = installed_version;
59 changes: 48 additions & 11 deletions src/install.rs
Original file line number Diff line number Diff line change
@@ -49,18 +49,25 @@ const SET_PATH_FILE_CONTENT: &str = indoc! {r#"
pub enum AssetType {
Client,
Node,
NodeManager,
Testnet,
}

impl AssetType {
pub fn variants() -> Vec<AssetType> {
vec![AssetType::Client, AssetType::Node, AssetType::Testnet]
vec![
AssetType::Client,
AssetType::Node,
AssetType::NodeManager,
AssetType::Testnet,
]
}

pub fn get_release_type(&self) -> ReleaseType {
match self {
AssetType::Client => ReleaseType::Safe,
AssetType::Node => ReleaseType::Safenode,
AssetType::NodeManager => ReleaseType::SafenodeManager,
AssetType::Testnet => ReleaseType::Testnet,
}
}
@@ -71,6 +78,7 @@ impl std::fmt::Display for AssetType {
match *self {
AssetType::Client => write!(f, "safe"),
AssetType::Node => write!(f, "safenode"),
AssetType::NodeManager => write!(f, "safenode-manager"),
AssetType::Testnet => write!(f, "testnet"),
}
}
@@ -82,6 +90,8 @@ pub struct Settings {
pub safe_version: String,
pub safenode_path: PathBuf,
pub safenode_version: String,
pub safenode_manager_path: PathBuf,
pub safenode_manager_version: String,
pub testnet_path: PathBuf,
pub testnet_version: String,
}
@@ -96,6 +106,8 @@ impl Settings {
safe_version: String::new(),
safenode_path: PathBuf::new(),
safenode_version: String::new(),
safenode_manager_path: PathBuf::new(),
safenode_manager_version: String::new(),
testnet_path: PathBuf::new(),
testnet_version: String::new(),
})
@@ -105,6 +117,8 @@ impl Settings {
safe_version: String::new(),
safenode_path: PathBuf::new(),
safenode_version: String::new(),
safenode_manager_path: PathBuf::new(),
safenode_manager_version: String::new(),
testnet_path: PathBuf::new(),
testnet_version: String::new(),
}
@@ -116,6 +130,7 @@ impl Settings {
match asset_type {
AssetType::Client => self.safe_version.clone(),
AssetType::Node => self.safenode_version.clone(),
AssetType::NodeManager => self.safenode_manager_version.clone(),
AssetType::Testnet => self.testnet_version.clone(),
}
}
@@ -124,6 +139,7 @@ impl Settings {
match asset_type {
AssetType::Client => !self.safe_version.is_empty(),
AssetType::Node => !self.safenode_version.is_empty(),
AssetType::NodeManager => !self.safenode_manager_version.is_empty(),
AssetType::Testnet => !self.testnet_version.is_empty(),
}
}
@@ -132,6 +148,7 @@ impl Settings {
match asset_type {
AssetType::Client => self.safe_path.clone(),
AssetType::Node => self.safenode_path.clone(),
AssetType::NodeManager => self.safenode_manager_path.clone(),
AssetType::Testnet => self.testnet_path.clone(),
}
}
@@ -341,6 +358,7 @@ fn get_bin_name(asset_type: &AssetType) -> String {
let mut bin_name = match asset_type {
AssetType::Client => "safe".to_string(),
AssetType::Node => "safenode".to_string(),
AssetType::NodeManager => "safenode-manager".to_string(),
AssetType::Testnet => "testnet".to_string(),
};
if OS == "windows" {
@@ -374,9 +392,6 @@ mod test {
};
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[cfg(windows)]
use std::path::PathBuf;
#[cfg(unix)]
use std::path::{Path, PathBuf};

/// These may seem pointless, but they are useful for when the tests run on different
@@ -440,7 +455,7 @@ mod test {
mock_release_repo
.expect_download_release_from_s3()
.with(
eq(&ReleaseType::Safenode),
eq(&ReleaseType::Safe),
eq(latest_version),
always(), // Varies per platform
eq(&ArchiveType::TarGz),
@@ -518,7 +533,7 @@ mod test {
mock_release_repo
.expect_download_release_from_s3()
.with(
eq(&ReleaseType::Safenode),
eq(&ReleaseType::Safe),
eq(latest_version),
always(), // Varies per platform
eq(&ArchiveType::TarGz),
@@ -580,7 +595,7 @@ mod test {
mock_release_repo
.expect_download_release_from_s3()
.with(
eq(&ReleaseType::Safenode),
eq(&ReleaseType::Safe),
eq(specific_version),
always(), // Varies per platform
eq(&ArchiveType::TarGz),
@@ -706,6 +721,8 @@ mod test {
safe_bin_file.write_binary(b"fake safe code")?;
let safenode_bin_file = tmp_data_path.child("safenode");
safenode_bin_file.write_binary(b"fake safenode code")?;
let safenode_manager_bin_file = tmp_data_path.child("safenode-manager");
safenode_manager_bin_file.write_binary(b"fake safenode-manager code")?;
let testnet_bin_file = tmp_data_path.child("testnet");
testnet_bin_file.write_binary(b"fake testnet code")?;

@@ -714,6 +731,8 @@ mod test {
safe_version: "v0.75.1".to_string(),
safenode_path: safenode_bin_file.to_path_buf(),
safenode_version: "v0.75.2".to_string(),
safenode_manager_path: safenode_manager_bin_file.to_path_buf(),
safenode_manager_version: "v0.1.8".to_string(),
testnet_path: testnet_bin_file.to_path_buf(),
testnet_version: "v0.75.3".to_string(),
};
@@ -726,6 +745,11 @@ mod test {
assert_eq!(settings.safe_version, "v0.75.1");
assert_eq!(settings.safenode_path, safenode_bin_file.to_path_buf());
assert_eq!(settings.safenode_version, "v0.75.2");
assert_eq!(
settings.safenode_manager_path,
safenode_manager_bin_file.to_path_buf()
);
assert_eq!(settings.safenode_manager_version, "v0.1.8");
assert_eq!(settings.testnet_path, testnet_bin_file.to_path_buf());
assert_eq!(settings.testnet_version, "v0.75.3");
Ok(())
@@ -744,6 +768,8 @@ mod test {
safe_bin_file.write_binary(b"fake safe code")?;
let safenode_bin_file = tmp_data_path.child("safenode");
safenode_bin_file.write_binary(b"fake safenode code")?;
let safenode_manager_bin_file = tmp_data_path.child("safenode-manager");
safenode_manager_bin_file.write_binary(b"fake safenode-manager code")?;
let testnet_bin_file = tmp_data_path.child("testnet");
testnet_bin_file.write_binary(b"fake testnet code")?;

@@ -752,6 +778,8 @@ mod test {
safe_version: "v0.75.1".to_string(),
safenode_path: safenode_bin_file.to_path_buf(),
safenode_version: "v0.75.2".to_string(),
safenode_manager_path: safenode_manager_bin_file.to_path_buf(),
safenode_manager_version: "v0.1.8".to_string(),
testnet_path: testnet_bin_file.to_path_buf(),
testnet_version: "v0.75.3".to_string(),
};
@@ -764,6 +792,11 @@ mod test {
assert_eq!(settings.safe_version, "v0.75.1");
assert_eq!(settings.safenode_path, safenode_bin_file.to_path_buf());
assert_eq!(settings.safenode_version, "v0.75.2");
assert_eq!(
settings.safenode_manager_path,
safenode_manager_bin_file.to_path_buf()
);
assert_eq!(settings.safenode_manager_version, "v0.1.8");
assert_eq!(settings.testnet_path, testnet_bin_file.to_path_buf());
assert_eq!(settings.testnet_version, "v0.75.3");
Ok(())
@@ -778,13 +811,12 @@ mod test {
{
"safe_path": "/home/chris/.local/safe",
"safe_version": "v0.75.1",
"safe_is_elevated_install": false,
"safenode_path": "/home/chris/.local/bin/safenode",
"safenode_version": "v0.75.2",
"safenode_is_elevated_install": false,
"safenode_manager_path": "/home/chris/.local/bin/safenode-manager",
"safenode_manager_version": "v0.1.8",
"testnet_path": "/home/chris/.local/bin/testnet",
"testnet_version": "v0.75.3",
"testnet_is_elevated_install": false
"testnet_version": "v0.75.3"
}
"#,
)?;
@@ -803,6 +835,11 @@ mod test {
assert_eq!(settings.safe_version, "v0.75.1");
assert_eq!(settings.safenode_path, safenode_bin_file.to_path_buf());
assert_eq!(settings.safenode_version, "v0.75.2");
assert_eq!(
settings.safenode_manager_path,
PathBuf::from("/home/chris/.local/bin/safenode-manager")
);
assert_eq!(settings.safenode_manager_version, "v0.1.8");
assert_eq!(
settings.testnet_path,
PathBuf::from("/home/chris/.local/bin/testnet")
43 changes: 43 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -77,6 +77,30 @@ enum Commands {
#[arg(short = 'v', long)]
version: Option<String>,
},
/// Install the safenode-manager binary.
///
/// The location is platform specific:
/// - Linux/macOS: $HOME/.local/bin
/// - Windows: C:\Users\<username>\safe
///
/// On Linux/macOS, the Bash shell profile will be modified to add $HOME/.local/bin to the PATH
/// variable. On Windows, the user Path variable will be modified to add C:\Users\<username>\safe.
#[clap(verbatim_doc_comment)]
NodeManager {
/// 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 testnet binary.
///
/// The location is platform specific:
@@ -139,6 +163,25 @@ async fn main() -> Result<()> {
install::check_prerequisites()?;
process_install_cmd(AssetType::Node, path, version, no_modify_shell_profile).await
}
Some(Commands::NodeManager {
path,
no_modify_shell_profile,
version,
}) => {
println!("**************************************");
println!("* *");
println!("* Installing safenode-manager *");
println!("* *");
println!("**************************************");
install::check_prerequisites()?;
process_install_cmd(
AssetType::NodeManager,
path,
version,
no_modify_shell_profile,
)
.await
}
Some(Commands::Testnet {
path,
no_modify_shell_profile,
52 changes: 52 additions & 0 deletions src/update.rs
Original file line number Diff line number Diff line change
@@ -56,6 +56,8 @@ mod test {
safe_version: String::new(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "v0.83.13".to_string(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.8".to_string(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "v0.3.4".to_string(),
};
@@ -67,6 +69,8 @@ mod test {
safe_version: "v0.78.26".to_string(),
safenode_path: PathBuf::new(),
safenode_version: String::new(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.8".to_string(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "v0.3.4".to_string(),
};
@@ -78,6 +82,21 @@ mod test {
safe_version: "v0.78.26".to_string(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "v0.83.13".to_string(),
safenode_manager_path: PathBuf::new(),
safenode_manager_version: String::new(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "v0.3.4".to_string(),
};
let decision = perform_update_assessment(&AssetType::NodeManager, "v0.1.8", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::NoPreviousInstallation);

let settings = Settings {
safe_path: PathBuf::from("/home/chris/.local/safe"),
safe_version: "v0.78.26".to_string(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "v0.83.13".to_string(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.8".to_string(),
testnet_path: PathBuf::new(),
testnet_version: String::new(),
};
@@ -94,6 +113,8 @@ mod test {
safe_version: "v0.78.26".to_string(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "v0.83.13".to_string(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.8".to_string(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "v0.3.4".to_string(),
};
@@ -104,6 +125,9 @@ mod test {
let decision = perform_update_assessment(&AssetType::Node, "v0.83.13", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::AtLatestVersion);

let decision = perform_update_assessment(&AssetType::NodeManager, "v0.1.8", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::AtLatestVersion);

let decision = perform_update_assessment(&AssetType::Testnet, "v0.3.4", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::AtLatestVersion);

@@ -118,6 +142,8 @@ mod test {
safe_version: "v0.78.26".to_string(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "v0.83.13".to_string(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.8".to_string(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "v0.3.4".to_string(),
};
@@ -140,6 +166,15 @@ mod test {
),
}

let result = perform_update_assessment(&AssetType::NodeManager, "v0.1.7", &settings);
match result {
Ok(_) => return Err(eyre!("this test should return an error")),
Err(e) => assert_eq!(
"The latest version is less than the current version of your binary.",
e.to_string()
),
}

let result = perform_update_assessment(&AssetType::Node, "v0.2.0", &settings);
match result {
Ok(_) => return Err(eyre!("this test should return an error")),
@@ -159,6 +194,8 @@ mod test {
safe_version: "v0.78.26".to_string(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "v0.83.13".to_string(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.7".to_string(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "v0.3.4".to_string(),
};
@@ -169,6 +206,9 @@ mod test {
let decision = perform_update_assessment(&AssetType::Node, "v0.83.14", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

let decision = perform_update_assessment(&AssetType::NodeManager, "v0.1.8", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

let decision = perform_update_assessment(&AssetType::Testnet, "v0.3.5", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

@@ -183,6 +223,8 @@ mod test {
safe_version: "v0.78.26".to_string(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "v0.83.13".to_string(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.7".to_string(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "v0.3.4".to_string(),
};
@@ -193,6 +235,9 @@ mod test {
let decision = perform_update_assessment(&AssetType::Node, "v0.84.0", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

let decision = perform_update_assessment(&AssetType::NodeManager, "v0.2.0", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

let decision = perform_update_assessment(&AssetType::Testnet, "v0.4.0", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

@@ -207,6 +252,8 @@ mod test {
safe_version: "v0.78.26".to_string(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "v0.83.13".to_string(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.7".to_string(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "v0.3.4".to_string(),
};
@@ -217,6 +264,9 @@ mod test {
let decision = perform_update_assessment(&AssetType::Node, "v1.0.0", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

let decision = perform_update_assessment(&AssetType::NodeManager, "v1.0.0", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

let decision = perform_update_assessment(&AssetType::Testnet, "v1.0.0", &settings)?;
assert_matches!(decision, UpdateAssessmentResult::PerformUpdate);

@@ -230,6 +280,8 @@ mod test {
safe_version: "0.78.26".to_string(),
safenode_path: PathBuf::from("/home/chris/.local/bin/safenode"),
safenode_version: "0.83.13".to_string(),
safenode_manager_path: PathBuf::from("/home/chris/.local/bin/safenode-manager"),
safenode_manager_version: "v0.1.7".to_string(),
testnet_path: PathBuf::from("/home/chris/.local/bin/testnet"),
testnet_version: "0.3.4".to_string(),
};

0 comments on commit d915e7e

Please sign in to comment.