Check and Lint / Check (pull_request) Successful in 18s
Check and Lint / Rustfmt (pull_request) Successful in 15s
Check and Lint / Clippy (pull_request) Successful in 19s
Test and Coverage / Test (pull_request) Successful in 20s
Check and Lint / Build RPM (pull_request) Successful in 28s
Test and Coverage / Coverage (pull_request) Successful in 41s
Check and Lint / Rustfmt (push) Successful in 16s
Check and Lint / Clippy (push) Successful in 19s
Check and Lint / Check (push) Successful in 24s
Test and Coverage / Test (push) Successful in 20s
Check and Lint / Build RPM (push) Successful in 28s
Release / Semantic release (push) Successful in 49s
Test and Coverage / Coverage (push) Successful in 44s
Release / Build and push RPM (push) Successful in 1m18s
141 lines
4.5 KiB
Rust
141 lines
4.5 KiB
Rust
mod config;
|
|
mod detect;
|
|
mod env_file;
|
|
mod launcher;
|
|
mod tui;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use anyhow::{Context, Error, Result, bail};
|
|
use clap::{ArgAction, Parser, Subcommand};
|
|
use config::{Config, LauncherEntry, load_config};
|
|
use detect::detect_beamline;
|
|
|
|
/// Config file installed by the RPM, preferred when it exists.
|
|
const INSTALLED_CONFIG: &str = "/etc/aarel/defaults.json";
|
|
/// Config file shipped in the source tree, used as a fallback.
|
|
const LOCAL_CONFIG: &str = "data/defaults.json";
|
|
/// Default env file added to the server's environment when launching.
|
|
const DEFAULT_ENV_FILE: &str = "/etc/aaredaq/aaredaq.env";
|
|
|
|
#[derive(Parser)]
|
|
#[command(name = "aarel", version, disable_version_flag = true)]
|
|
struct Cli {
|
|
/// Print version information.
|
|
#[arg(short = 'v', long = "version", action = ArgAction::Version)]
|
|
version: Option<bool>,
|
|
|
|
/// Path to the launcher config file. Defaults to /etc/aarel/defaults.json,
|
|
/// falling back to ./data/defaults.json.
|
|
#[arg(long)]
|
|
config: Option<PathBuf>,
|
|
|
|
#[command(subcommand)]
|
|
command: Command,
|
|
}
|
|
|
|
/// Resolve the config path: the explicit `--config`, else the installed config
|
|
/// if present, else the local one.
|
|
fn resolve_config_path(config: Option<PathBuf>) -> PathBuf {
|
|
if let Some(path) = config {
|
|
return path;
|
|
}
|
|
let installed = PathBuf::from(INSTALLED_CONFIG);
|
|
if installed.is_file() {
|
|
installed
|
|
} else {
|
|
PathBuf::from(LOCAL_CONFIG)
|
|
}
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Command {
|
|
/// Enter the TUI to choose what to launch
|
|
#[command(alias = "p")]
|
|
Pick,
|
|
/// Launch the AareGUI with default settings
|
|
#[command(alias = "g")]
|
|
Gui {
|
|
/// Deployment to launch. Defaults to the detected beamline.
|
|
#[arg(short = 'd', long)]
|
|
deployment: Option<String>,
|
|
},
|
|
/// Launch the AareDAQ server with default settings
|
|
#[command(alias = "s")]
|
|
Server {
|
|
/// Deployment to launch. Defaults to the detected beamline.
|
|
#[arg(short = 'd', long)]
|
|
deployment: Option<String>,
|
|
|
|
/// Env file (`key=value` per line) added to the server's environment.
|
|
/// Ignored with a warning if it does not exist.
|
|
#[arg(long, default_value = DEFAULT_ENV_FILE)]
|
|
env_file: PathBuf,
|
|
},
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
let cli = Cli::parse();
|
|
|
|
let config_path = resolve_config_path(cli.config);
|
|
let json = std::fs::read_to_string(&config_path)
|
|
.with_context(|| format!("failed to read config file {}", config_path.display()))?;
|
|
let mut config = load_config(&json)?;
|
|
|
|
check_base_exists("deployments_base", &config.deployments_base, &config_path)?;
|
|
|
|
// Populate the config with deployments found on disk so they are available
|
|
// to both `lookup_entry` and the TUI. Configured entries take precedence.
|
|
for (label, entry) in config.discovered_entries() {
|
|
config.entries.entry(label).or_insert(entry);
|
|
}
|
|
|
|
match cli.command {
|
|
Command::Pick => {
|
|
if let Some(launch) = tui::run(&config, DEFAULT_ENV_FILE)? {
|
|
let env = launch
|
|
.env_file
|
|
.as_deref()
|
|
.map(env_file::load_env_file)
|
|
.unwrap_or_default();
|
|
launcher::launch_with_env(&launch.python, &launch.entrypoint, &env)?;
|
|
}
|
|
}
|
|
Command::Gui { deployment } => launcher::launch_gui(lookup_entry(&config, deployment)?)?,
|
|
Command::Server {
|
|
deployment,
|
|
env_file,
|
|
} => {
|
|
let env = env_file::load_env_file(&env_file);
|
|
launcher::launch_server(lookup_entry(&config, deployment)?, &env)?
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Fail with a helpful message if a configured base directory is missing.
|
|
fn check_base_exists(field: &str, base: &Path, config_file: &Path) -> Result<()> {
|
|
if !base.is_dir() {
|
|
bail!(
|
|
"{field} '{}' does not exist — edit '{field}' in {} to point at a valid directory",
|
|
base.display(),
|
|
config_file.display(),
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Look up the launcher entry to use: the explicitly requested `entry`, or the
|
|
/// detected beamline when none was given.
|
|
fn lookup_entry(config: &Config, entry: Option<String>) -> Result<&LauncherEntry> {
|
|
let beamline = match entry {
|
|
Some(entry) => entry,
|
|
None => detect_beamline().map_err(Error::msg)?,
|
|
};
|
|
config
|
|
.entries
|
|
.get(&beamline)
|
|
.with_context(|| format!("no config entry for beamline '{beamline}'"))
|
|
}
|