Skip to content

Commit

Permalink
feat: provide autonomi-launcher binary
Browse files Browse the repository at this point in the history
This small binary attempts to detect the presence of a terminal executable, then uses it to run the
`node-launchpad` TUI inside it.

The TUI is downloaded and stored at `~/.local/bin/autonomi-launcher` on Linux/macOS and
`%USERPROFILE%\safe\autonomi-launcher.exe` on Windows. Right now, the version it downloads is hard
coded to `0.1.0-alpha.0`. This can be changed quite easily once we get a new release.

The launcher is added to the release process as a new binary. However, there is a complication not
yet dealt with. The `autonomi-launcher` and `node-launchpad` binaries are in the same crate, so the
process for uploading assets to Github releases will need to be modified to accommodate that. We can
come back to it.
  • Loading branch information
jacderida committed May 4, 2024
1 parent 7c9be28 commit a58c73a
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 18 deletions.
29 changes: 25 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ build-release-artifacts arch:
cross build --release --target $arch --bin faucet --features=distribution
cross build --release --target $arch --bin safenode_rpc_client
cross build --release --target $arch --bin node-launchpad
cross build --release --target $arch --bin autonomi-launcher
else
cargo build --release --features="network-contacts,distribution" --target $arch --bin safe
cargo build --release --features=network-contacts --target $arch --bin safenode
Expand All @@ -128,6 +129,7 @@ build-release-artifacts arch:
cargo build --release --target $arch --bin faucet --features=distribution
cargo build --release --target $arch --bin safenode_rpc_client
cargo build --release --target $arch --bin node-launchpad
cargo build --release --target $arch --bin autonomi-launcher
fi

find target/$arch/release -maxdepth 1 -type f -exec cp '{}' artifacts \;
Expand Down Expand Up @@ -172,7 +174,14 @@ package-release-assets bin version="":
bin="{{bin}}"

supported_bins=(\
"safe" "safenode" "safenode-manager" "safenodemand" "faucet" "safenode_rpc_client" "node-launchpad")
"safe" \
"safenode" \
"safenode-manager" \
"safenodemand" \
"faucet" \
"safenode_rpc_client" \
"node-launchpad" \
"autonomi-launcher")
crate_dir_name=""

# In the case of the node manager, the actual name of the crate is `sn-node-manager`, but the
Expand Down Expand Up @@ -200,6 +209,9 @@ package-release-assets bin version="":
node-launchpad)
crate_dir_name="sn_node_launchpad"
;;
autonomi-launcher)
crate_dir_name="sn_node_launchpad"
;;
*)
echo "The $bin binary is not supported"
exit 1
Expand Down Expand Up @@ -331,6 +343,9 @@ upload-release-assets-to-s3 bin_name:
node-launchpad)
bucket="sn-node-launchpad"
;;
autonomi-launcher-launchpad)
bucket="sn-node-launchpad"
;;
*)
echo "The {{bin_name}} binary is not supported"
exit 1
Expand Down
7 changes: 7 additions & 0 deletions sn_node_launchpad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ build = "build.rs"
name = "node-launchpad"
path = "src/bin/tui/main.rs"

[[bin]]
name = "autonomi-launcher"
path = "src/bin/launcher/main.rs"

[dependencies]
better-panic = "0.3.0"
clap = { version = "4.4.5", features = [
Expand All @@ -29,6 +33,7 @@ config = "0.14.0"
crossterm = { version = "0.27.0", features = ["serde", "event-stream"] }
derive_deref = "1.1.1"
directories = "5.0.1"
dirs-next = "~2.0.0"
futures = "0.3.28"
human-panic = "1.2.0"
json5 = "0.4.1"
Expand All @@ -42,6 +47,7 @@ serde_json = "1.0.107"
signal-hook = "0.3.17"
sn-node-manager = { version = "0.7.4", path = "../sn_node_manager" }
sn_peers_acquisition = { version = "0.2.10", path = "../sn_peers_acquisition" }
sn-releases = "0.2.1"
sn_service_management = { version = "0.2.4", path = "../sn_service_management" }
strip-ansi-escapes = "0.2.0"
strum = { version = "0.26.1", features = ["derive"] }
Expand All @@ -51,6 +57,7 @@ tracing = "0.1.37"
tracing-error = "0.2.0"
tracing-subscriber = { version = "0.3.17", features = ["env-filter", "serde"] }
tui-input = "0.8.0"
which = "6.0.1"

[build-dependencies]
vergen = { version = "8.2.6", features = ["build", "git", "gitoxide", "cargo"] }
206 changes: 206 additions & 0 deletions sn_node_launchpad/src/bin/launcher/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright 2024 MaidSafe.net limited.
//
// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use clap::Parser;
use color_eyre::{eyre::eyre, eyre::Result};

use sn_node_manager::{
helpers::{create_temp_dir, download_and_extract_release},
VerbosityLevel,
};
use sn_releases::{ReleaseType, SafeReleaseRepoActions};
use std::{
path::{Path, PathBuf},
process::Command,
};
use which::which;

#[derive(Debug)]
enum TerminalType {
Alacritty(PathBuf),
Gnome(PathBuf),
ITerm2(PathBuf),
Kitty(PathBuf),
Konsole(PathBuf),
MacOS(PathBuf),
WindowsCmd(PathBuf),
WindowsPowershell(PathBuf),
WindowsTerminal(PathBuf),
Xterm(PathBuf),
}

fn detect_terminal() -> Result<TerminalType> {
if cfg!(target_os = "windows") {
if let Ok(path) = which("wt.exe") {
Ok(TerminalType::WindowsTerminal(path))
} else if let Ok(path) = which("powershell.exe") {
Ok(TerminalType::WindowsPowershell(path))
} else if let Ok(path) = which("cmd.exe") {
Ok(TerminalType::WindowsCmd(path))
} else {
Err(eyre!("Could not find suitable terminal on Windows"))
}
} else if cfg!(target_os = "macos") {
if which("iTerm.app").is_ok() {
Ok(TerminalType::ITerm2(PathBuf::from("osascript")))
} else {
Ok(TerminalType::MacOS(PathBuf::from("osascript")))
}
} else {
get_linux_terminal()
}
}

fn get_linux_terminal() -> Result<TerminalType> {
match std::env::var("TERM") {
Ok(val) => {
if let Ok(path) = which(val.clone()) {
match val.as_str() {
"alacritty" => Ok(TerminalType::Alacritty(path)),
"gnome" => Ok(TerminalType::Gnome(path)),
"kitty" => Ok(TerminalType::Kitty(path)),
"konsole" => Ok(TerminalType::Konsole(path)),
"xterm" => Ok(TerminalType::Xterm(path)),
"xterm-256color" => Ok(TerminalType::Xterm(path)),
_ => Err(eyre!("Terminal '{val}' is not supported")),
}
} else {
try_available_linux_terminals()
}
}
Err(_) => try_available_linux_terminals(),
}
}

fn try_available_linux_terminals() -> Result<TerminalType> {
if let Ok(path) = which("alacritty") {
Ok(TerminalType::Alacritty(path))
} else if let Ok(path) = which("gnome-terminal") {
Ok(TerminalType::Gnome(path))
} else if let Ok(path) = which("kitty") {
Ok(TerminalType::Kitty(path))
} else if let Ok(path) = which("konsole") {
Ok(TerminalType::Konsole(path))
} else if let Ok(path) = which("xterm") {
Ok(TerminalType::Xterm(path))
} else if let Ok(path) = which("xterm-256color") {
Ok(TerminalType::Xterm(path))
} else {
Err(eyre!("Could not find terminal on Linux"))
}
}

fn launch_terminal(terminal_type: &TerminalType, launchpad_path: &Path) -> Result<()> {
let launchpad_path = launchpad_path.to_string_lossy().to_string();
match terminal_type {
TerminalType::Kitty(path) | TerminalType::Konsole(path) | TerminalType::Xterm(path) => {
Command::new(path).arg("-e").arg(launchpad_path).spawn()?;
Ok(())
}
TerminalType::Alacritty(path) => {
Command::new(path)
.arg("--command")
.arg("sudo")
.arg("sh")
.arg("-c")
.arg(launchpad_path)
.spawn()?;
Ok(())
}
TerminalType::Gnome(path) => {
Command::new(path)
.arg("--")
.arg("sudo")
.arg(launchpad_path)
.spawn()?;
Ok(())
}
TerminalType::MacOS(path) => {
Command::new(path)
.arg("-e")
.arg(format!(
"tell application \"Terminal\" to do script \"sudo {}\"",
launchpad_path
))
.spawn()?;
Ok(())
}
TerminalType::ITerm2(path) => {
Command::new(path)
.arg("-e")
.arg(format!("tell application \"iTerm\" to create window with default profile command \"sudo {}\"", launchpad_path))
.spawn()?;
Ok(())
}
TerminalType::WindowsCmd(path)
| TerminalType::WindowsPowershell(path)
| TerminalType::WindowsTerminal(path) => {
Command::new(path).arg("/c").arg(launchpad_path).spawn()?;
Ok(())
}
}
}

#[derive(Parser, Debug)]
#[command(author, about)]
pub struct Cli {
#[arg(long)]
pub launchpad_path: Option<PathBuf>,
#[arg(long)]
pub launchpad_version: Option<String>,
}

#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::parse();
let launchpad_path = if let Some(path) = args.launchpad_path {
path
} else {
let path = get_node_launchpad_path()?;
if path.exists() {
path
} else {
println!("Retrieving latest version of Node Launchpad...");
let release_repo = <dyn SafeReleaseRepoActions>::default_config();
let (bin_path, _) = download_and_extract_release(
ReleaseType::NodeLaunchpad,
None,
None,
&*release_repo,
VerbosityLevel::Normal,
Some(create_temp_dir()?),
)
.await?;

std::fs::copy(bin_path, path.clone())?;
path
}
};

let terminal_type = detect_terminal()?;
launch_terminal(&terminal_type, &launchpad_path)?;
Ok(())
}

#[cfg(target_os = "windows")]
fn get_node_launchpad_path() -> Result<PathBuf> {
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");
std::fs::create_dir_all(safe_dir_path.clone())?;
Ok(safe_dir_path.join("node-launchpad.exe"))
}

#[cfg(target_family = "unix")]
fn get_node_launchpad_path() -> Result<PathBuf> {
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(".local").join("bin");
std::fs::create_dir_all(safe_dir_path.clone())?;
Ok(safe_dir_path.join("node-launchpad.exe"))
}
1 change: 1 addition & 0 deletions sn_node_manager/src/cmd/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub async fn add(
version,
&*release_repo,
verbosity,
None,
)
.await?
};
Expand Down
1 change: 1 addition & 0 deletions sn_node_manager/src/cmd/faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub async fn add(
version,
&*release_repo,
verbosity,
None,
)
.await?
};
Expand Down
Loading

0 comments on commit a58c73a

Please sign in to comment.