提交 d24cb818 authored 作者: Serhij S's avatar Serhij S

set env in exec mode

上级 317510be
...@@ -695,7 +695,7 @@ dependencies = [ ...@@ -695,7 +695,7 @@ dependencies = [
[[package]] [[package]]
name = "roboplc-cli" name = "roboplc-cli"
version = "0.4.5" version = "0.4.6"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"clap", "clap",
......
[package] [package]
name = "roboplc-cli" name = "roboplc-cli"
version = "0.4.5" version = "0.4.6"
edition = "2021" edition = "2021"
authors = ["Serhij S. <div@altertech.com>"] authors = ["Serhij S. <div@altertech.com>"]
license = "Apache-2.0" license = "Apache-2.0"
......
use std::path::PathBuf; use std::{collections::BTreeMap, path::PathBuf};
use clap::{Parser, ValueEnum}; use clap::{Parser, ValueEnum};
...@@ -158,6 +158,12 @@ pub struct ExecCommand { ...@@ -158,6 +158,12 @@ pub struct ExecCommand {
help = "Force execute (ignore if other program is being executed)" help = "Force execute (ignore if other program is being executed)"
)] )]
pub force: bool, pub force: bool,
#[clap(
short = 'e',
long,
help = "Environment variable to pass to the program, as NAME=VALUE"
)]
pub env: Vec<String>,
#[arg( #[arg(
trailing_var_arg = true, trailing_var_arg = true,
allow_hyphen_values = true, allow_hyphen_values = true,
...@@ -174,6 +180,7 @@ pub struct FlashExec { ...@@ -174,6 +180,7 @@ pub struct FlashExec {
pub force: bool, pub force: bool,
pub run: bool, pub run: bool,
pub program_args: Vec<String>, pub program_args: Vec<String>,
pub program_env: BTreeMap<String, String>,
} }
impl From<FlashCommand> for FlashExec { impl From<FlashCommand> for FlashExec {
...@@ -186,12 +193,26 @@ impl From<FlashCommand> for FlashExec { ...@@ -186,12 +193,26 @@ impl From<FlashCommand> for FlashExec {
force: cmd.force, force: cmd.force,
run: cmd.run, run: cmd.run,
program_args: Vec::new(), program_args: Vec::new(),
program_env: BTreeMap::new(),
} }
} }
} }
impl From<ExecCommand> for FlashExec { impl From<ExecCommand> for FlashExec {
fn from(cmd: ExecCommand) -> Self { fn from(cmd: ExecCommand) -> Self {
let program_env = cmd
.env
.iter()
.map(|s| {
let mut parts = s.splitn(2, '=');
let key = parts.next().unwrap().to_string();
let value = parts
.next()
.expect("No environment variable value set")
.to_string();
(key, value)
})
.collect();
Self { Self {
cargo: cmd.cargo, cargo: cmd.cargo,
cargo_target: cmd.cargo_target, cargo_target: cmd.cargo_target,
...@@ -200,6 +221,7 @@ impl From<ExecCommand> for FlashExec { ...@@ -200,6 +221,7 @@ impl From<ExecCommand> for FlashExec {
force: cmd.force, force: cmd.force,
run: false, run: false,
program_args: cmd.args, program_args: cmd.args,
program_env,
} }
} }
} }
use std::io::Write as _;
use std::path::Path; use std::path::Path;
use std::{collections::BTreeMap, io::Write as _};
use colored::Colorize; use colored::Colorize;
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
...@@ -7,7 +7,6 @@ use tokio::io::AsyncReadExt as _; ...@@ -7,7 +7,6 @@ use tokio::io::AsyncReadExt as _;
use futures_util::{SinkExt, StreamExt}; use futures_util::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::tungstenite::Message;
pub fn exec( pub fn exec(
...@@ -16,11 +15,12 @@ pub fn exec( ...@@ -16,11 +15,12 @@ pub fn exec(
file: &Path, file: &Path,
force: bool, force: bool,
args: Vec<String>, args: Vec<String>,
env: BTreeMap<String, String>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
tokio::runtime::Builder::new_current_thread() tokio::runtime::Builder::new_current_thread()
.enable_all() .enable_all()
.build()? .build()?
.block_on(exec_remote(url, key, file, force, args))?; .block_on(exec_remote(url, key, file, force, args, env))?;
Ok(()) Ok(())
} }
...@@ -31,6 +31,24 @@ enum Output { ...@@ -31,6 +31,24 @@ enum Output {
Terminated(i32), Terminated(i32),
} }
#[derive(Serialize)]
struct ExecPayload<'a> {
k: &'a str,
force: bool,
#[serde(skip_serializing_if = "Vec::is_empty")]
args: Vec<String>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
env: BTreeMap<String, String>,
term: ExecTerm,
}
#[derive(Serialize)]
struct ExecTerm {
width: usize,
height: usize,
name: String,
}
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
async fn exec_remote( async fn exec_remote(
url: &str, url: &str,
...@@ -38,6 +56,7 @@ async fn exec_remote( ...@@ -38,6 +56,7 @@ async fn exec_remote(
file: &Path, file: &Path,
force: bool, force: bool,
args: Vec<String>, args: Vec<String>,
env: BTreeMap<String, String>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let (ws_uri, url_short) = if let Some(u) = url.strip_prefix("http://") { let (ws_uri, url_short) = if let Some(u) = url.strip_prefix("http://") {
(format!("ws://{}/roboplc/api/ws.execute", u), u) (format!("ws://{}/roboplc/api/ws.execute", u), u)
...@@ -50,16 +69,17 @@ async fn exec_remote( ...@@ -50,16 +69,17 @@ async fn exec_remote(
println!(); println!();
let (mut socket, _) = tokio_tungstenite::connect_async(&ws_uri).await?; let (mut socket, _) = tokio_tungstenite::connect_async(&ws_uri).await?;
let (width, height) = term_size::dimensions().ok_or("Failed to get terminal size")?; let (width, height) = term_size::dimensions().ok_or("Failed to get terminal size")?;
let payload = json!({ let payload = ExecPayload {
"k": key, k: key,
"force": force, force,
"args": args, args,
"term": { env,
"width": width, term: ExecTerm {
"height": height, width,
"name": std::env::var("TERM").unwrap_or("xterm-256color".to_string()), height,
name: std::env::var("TERM").unwrap_or("xterm-256color".to_string()),
}, },
}); };
socket socket
.send(Message::Text(serde_json::to_string(&payload)?)) .send(Message::Text(serde_json::to_string(&payload)?))
.await?; .await?;
......
use std::{ use std::{
collections::BTreeMap,
env, fs, env, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
...@@ -27,12 +28,13 @@ fn flash_file( ...@@ -27,12 +28,13 @@ fn flash_file(
run: bool, run: bool,
exec_only: bool, exec_only: bool,
program_args: Vec<String>, program_args: Vec<String>,
program_env: BTreeMap<String, String>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
if !file.exists() { if !file.exists() {
return Err(format!("File not found: {}", file.display()).into()); return Err(format!("File not found: {}", file.display()).into());
} }
if exec_only { if exec_only {
return crate::exec::exec(url, key, file, force, program_args); return crate::exec::exec(url, key, file, force, program_args, program_env);
} }
if let Some(docker_img) = url.strip_prefix("docker://") { if let Some(docker_img) = url.strip_prefix("docker://") {
let tag = std::env::var("ROBOPLC_DOCKER_TAG").unwrap_or_else(|_| { let tag = std::env::var("ROBOPLC_DOCKER_TAG").unwrap_or_else(|_| {
...@@ -113,6 +115,7 @@ fn run_build_custom( ...@@ -113,6 +115,7 @@ fn run_build_custom(
file: &Path, file: &Path,
exec_only: bool, exec_only: bool,
program_args: Vec<String>, program_args: Vec<String>,
program_env: BTreeMap<String, String>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
println!("Remote: {}", url.yellow()); println!("Remote: {}", url.yellow());
println!("Build command line: {}", cmd.yellow()); println!("Build command line: {}", cmd.yellow());
...@@ -128,7 +131,17 @@ fn run_build_custom( ...@@ -128,7 +131,17 @@ fn run_build_custom(
if !file.exists() { if !file.exists() {
return Err(format!("File not found: {}", file.display()).into()); return Err(format!("File not found: {}", file.display()).into());
} }
flash_file(url, key, agent, file, force, run, exec_only, program_args)?; flash_file(
url,
key,
agent,
file,
force,
run,
exec_only,
program_args,
program_env,
)?;
Ok(()) Ok(())
} }
...@@ -152,6 +165,7 @@ pub fn flash( ...@@ -152,6 +165,7 @@ pub fn flash(
opts.run, opts.run,
exec_only, exec_only,
opts.program_args, opts.program_args,
opts.program_env,
)?; )?;
} else if let Some(custom_cmd) = build_custom.command { } else if let Some(custom_cmd) = build_custom.command {
run_build_custom( run_build_custom(
...@@ -166,6 +180,7 @@ pub fn flash( ...@@ -166,6 +180,7 @@ pub fn flash(
.ok_or("Custom build command requires a file")?, .ok_or("Custom build command requires a file")?,
exec_only, exec_only,
opts.program_args, opts.program_args,
opts.program_env,
)?; )?;
} else { } else {
let mut cargo_target: Option<String> = None; let mut cargo_target: Option<String> = None;
...@@ -240,6 +255,7 @@ pub fn flash( ...@@ -240,6 +255,7 @@ pub fn flash(
opts.run, opts.run,
exec_only, exec_only,
opts.program_args, opts.program_args,
opts.program_env,
)?; )?;
} }
report_ok() report_ok()
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论