Compare commits
66 Commits
a971b223b8
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
f8ec8a7e2e
|
|||
|
6a4cd86e25
|
|||
|
69288f9203
|
|||
|
d581809696
|
|||
|
ae8266db07
|
|||
|
47677f4705
|
|||
|
35a7972673
|
|||
|
11b4e6c005
|
|||
|
39c1d0a68c
|
|||
|
2e4c47e936
|
|||
|
18d55793d9
|
|||
|
506df24aae
|
|||
|
012f75e765
|
|||
|
2c363e9db6
|
|||
|
1b3c385794
|
|||
|
91355c1972
|
|||
|
6543edd395
|
|||
|
18282e2223
|
|||
|
3ace53b509
|
|||
|
affcb233bd
|
|||
|
b75e350db1
|
|||
|
10a9364f96
|
|||
|
c1ae6114b3
|
|||
|
e283a210ad
|
|||
|
2d620e963e
|
|||
|
e8757f827a
|
|||
|
9f45e784fc
|
|||
|
6aeab782ee
|
|||
| 5352bdd4ef | |||
|
a0034d9743
|
|||
|
fd01aae079
|
|||
|
c43bf1c453
|
|||
|
1f23f684a4
|
|||
|
1e9af40ea9
|
|||
|
107060929c
|
|||
|
59320043df
|
|||
|
a8eb20dbb1
|
|||
|
38ab32052d
|
|||
|
b1ef84209e
|
|||
|
10067174fc
|
|||
|
dd89d5a579
|
|||
|
b9a12883c1
|
|||
|
f0463e4eda
|
|||
|
47aea122e8
|
|||
|
439eb7d3c3
|
|||
|
93de208e17
|
|||
|
71cef0d1a9
|
|||
|
a1f7be1aa4
|
|||
|
049163d511
|
|||
|
bfed66a7ae
|
|||
|
f85c7a68b7
|
|||
|
5a8b23cd2d
|
|||
|
bb80fbabc9
|
|||
|
20287855b4
|
|||
|
d2249b6cac
|
|||
|
28e47063bd
|
|||
|
890a3f0385
|
|||
|
768a581e6a
|
|||
|
0e5a26d1f3
|
|||
|
efa4f950bf
|
|||
|
4affb0f702
|
|||
|
24a0dd4bfd
|
|||
|
a978e1fba8
|
|||
|
405d3f50ca
|
|||
|
60283c7b40
|
|||
|
9c8d0acd25
|
2030
Cargo.lock
generated
2030
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
19
Cargo.toml
19
Cargo.toml
@@ -1,12 +1,25 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "runrs"
|
name = "runrs"
|
||||||
version = "0.1.0"
|
version = "1.1.6"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
description = "Run Rust Scripts"
|
description = "A Tool for Run Rust Scripts"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["switch-rust-lang"]
|
||||||
|
switch-rust-lang = []
|
||||||
|
switch-go-lang = []
|
||||||
|
switch-ts-lang = []
|
||||||
|
switch-dart-lang = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rust_util = "0.6.41"
|
reqwest = { version = "0.12", features = ["blocking"] }
|
||||||
|
rust_util = "0.6"
|
||||||
|
sha256 = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
argh = "0.1"
|
||||||
|
script-sign = "0.1"
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -2,5 +2,28 @@
|
|||||||
|
|
||||||
Run rust script
|
Run rust script
|
||||||
|
|
||||||
|
Install:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo install --git https://git.hatter.ink/hatter/runrs runrs
|
||||||
|
```
|
||||||
|
|
||||||
|
Environment variables
|
||||||
|
|
||||||
|
| Env Key | Env Value |
|
||||||
|
|---------------|------------------------------------------------------------------------|
|
||||||
|
| `HOME` | User home, default by OS system |
|
||||||
|
| `LOGGER` | Logger, `*` for verbose |
|
||||||
|
| `RUNRS_SKIP_CACHE` | Skip compiled cached file, `bool` , turn on `true`, `yes`, `on` or `1` |
|
||||||
|
| `RUNRS_REBUILD` | Force rebuild, `bool` |
|
||||||
|
| `RUNRS_SILENT_BUILD` | Build new binary in silent mode, `bool` |
|
||||||
|
| `RUNRS_RUST_SCRIPT` | `rust_script` command line bin file |
|
||||||
|
| `RUNRS_MAX_SCRIPT_LEN` | Max script length, default 1MB |
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
Runnable binaries:
|
||||||
|
* ~/.cargo/bin/runrs
|
||||||
|
* ~/.cargo/bin/rust-script
|
||||||
|
|
||||||
Simply call `rust-script` (https://github.com/fornwall/rust-script).
|
Simply call `rust-script` (https://github.com/fornwall/rust-script).
|
||||||
2
external/rust-script/src/main.rs
vendored
2
external/rust-script/src/main.rs
vendored
@@ -319,7 +319,7 @@ fn parse_args() -> Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_env_on(env: &str, default: bool) -> bool {
|
fn is_env_on(env: &str, default: bool) -> bool {
|
||||||
std::env::var("CARGO_OUTPUT").map(|v| {
|
std::env::var(env).map(|v| {
|
||||||
let v = v.to_lowercase();
|
let v = v.to_lowercase();
|
||||||
v == "true" || v == "yes" || v == "1"
|
v == "true" || v == "yes" || v == "1"
|
||||||
}).unwrap_or_else(|_| default)
|
}).unwrap_or_else(|_| default)
|
||||||
|
|||||||
53
justfile
Normal file
53
justfile
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
_:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
install:
|
||||||
|
cargo install --path .
|
||||||
|
|
||||||
|
run-rust *args:
|
||||||
|
cargo run -- {{args}}
|
||||||
|
|
||||||
|
run-go *args:
|
||||||
|
cargo run --no-default-features --features switch-go-lang -- {{args}}
|
||||||
|
|
||||||
|
run-ts *args:
|
||||||
|
cargo run --no-default-features --features switch-ts-lang -- {{args}}
|
||||||
|
|
||||||
|
run-dart *args:
|
||||||
|
cargo run --no-default-features --features switch-dart-lang -- {{args}}
|
||||||
|
|
||||||
|
build-run-rust:
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
build-run-go:
|
||||||
|
cargo build --release --no-default-features --features switch-go-lang
|
||||||
|
|
||||||
|
build-run-ts:
|
||||||
|
cargo build --release --no-default-features --features switch-ts-lang
|
||||||
|
|
||||||
|
build-run-dart:
|
||||||
|
cargo build --release --no-default-features --features switch-dart-lang
|
||||||
|
|
||||||
|
install-run-go:
|
||||||
|
cargo build --release --no-default-features --features switch-go-lang
|
||||||
|
cp target/release/runrs ~/bin/rungo
|
||||||
|
|
||||||
|
install-run-ts:
|
||||||
|
cargo build --release --no-default-features --features switch-ts-lang
|
||||||
|
cp target/release/runrs ~/bin/runts
|
||||||
|
|
||||||
|
install-run-dart:
|
||||||
|
cargo build --release --no-default-features --features switch-dart-lang
|
||||||
|
cp target/release/runrs ~/bin/rundart
|
||||||
|
|
||||||
|
build-all:
|
||||||
|
@just build-run-rust
|
||||||
|
@just build-run-go
|
||||||
|
@just build-run-ts
|
||||||
|
@just build-run-dart
|
||||||
|
|
||||||
|
install-all:
|
||||||
|
@just install
|
||||||
|
@just install-run-go
|
||||||
|
@just install-run-ts
|
||||||
|
@just install-run-dart
|
||||||
20
src/help.rs
Normal file
20
src/help.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
pub fn print_help() {
|
||||||
|
let help = get_help()
|
||||||
|
.replace("{CARGO_PKG_NAME}", env!("CARGO_PKG_NAME"))
|
||||||
|
.replace("{CARGO_PKG_VERSION}", env!("CARGO_PKG_VERSION"))
|
||||||
|
.replace("{CARGO_PKG_DESCRIPTION}", env!("CARGO_PKG_DESCRIPTION"));
|
||||||
|
println!("{}", help);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
fn get_help() -> &'static str {
|
||||||
|
#[cfg(feature = "switch-rust-lang")]
|
||||||
|
return include_str!("script.help.rs.txt");
|
||||||
|
#[cfg(feature = "switch-go-lang")]
|
||||||
|
return include_str!("script.help.go.txt");
|
||||||
|
#[cfg(feature = "switch-ts-lang")]
|
||||||
|
return include_str!("script.help.ts.txt");
|
||||||
|
#[cfg(feature = "switch-dart-lang")]
|
||||||
|
return include_str!("script.help.dart.txt");
|
||||||
|
panic!("Unknown feature assigned.");
|
||||||
|
}
|
||||||
98
src/install.rs
Normal file
98
src/install.rs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
use crate::util;
|
||||||
|
use crate::util::ScriptMeta;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub fn install_scripts(script_repo: &Option<String>, scripts: &[String]) {
|
||||||
|
if scripts.is_empty() {
|
||||||
|
failure_and_exit!("No scripts assigned.");
|
||||||
|
}
|
||||||
|
let script_meta_map = util::fetch_script_meta_or_die(script_repo);
|
||||||
|
for (i, script_name) in scripts.iter().enumerate() {
|
||||||
|
information!(
|
||||||
|
"Installing script: {} [ {} / {} ]",
|
||||||
|
script_name,
|
||||||
|
(i + 1),
|
||||||
|
scripts.len()
|
||||||
|
);
|
||||||
|
install_script(script_name, &script_meta_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_script(script_name: &str, script_meta_map: &BTreeMap<String, ScriptMeta>) {
|
||||||
|
let script_meta = match script_meta_map.get(script_name) {
|
||||||
|
None => {
|
||||||
|
failure!("Script not found: {}", script_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(script_meta) => script_meta,
|
||||||
|
};
|
||||||
|
|
||||||
|
let script_url = &script_meta.script_full_url;
|
||||||
|
information!("Script URL: {}", &script_url);
|
||||||
|
|
||||||
|
let get_script_response_result = reqwest::blocking::get(script_url);
|
||||||
|
debugging!("Get script response: {:#?}", &get_script_response_result);
|
||||||
|
|
||||||
|
let get_script_response = match get_script_response_result {
|
||||||
|
Err(e) => {
|
||||||
|
failure!("Get script failed: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(response) => response,
|
||||||
|
};
|
||||||
|
let get_script_response_status = get_script_response.status().as_u16();
|
||||||
|
if get_script_response_status == 404 {
|
||||||
|
failure!("Script not found!");
|
||||||
|
return;
|
||||||
|
} else if get_script_response_status != 200 {
|
||||||
|
failure!("Get script failed: {}", get_script_response_status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let remote_script_content = match get_script_response.text() {
|
||||||
|
Err(e) => {
|
||||||
|
failure!("Get script: {} failed: {}", &script_url, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(remote_script_content) => remote_script_content,
|
||||||
|
};
|
||||||
|
let user_home = util::get_user_home_or_die();
|
||||||
|
let output_file_name = PathBuf::from(&user_home)
|
||||||
|
.join("bin")
|
||||||
|
.join(&script_meta.script_name);
|
||||||
|
if output_file_name.exists() {
|
||||||
|
warning!("Script file: {} exists", output_file_name.to_string_lossy());
|
||||||
|
}
|
||||||
|
if let Ok(script_content) = fs::read_to_string(&output_file_name) {
|
||||||
|
if remote_script_content == script_content {
|
||||||
|
success!(
|
||||||
|
"File is extract same, skip write file: {}",
|
||||||
|
output_file_name.to_string_lossy()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(e) = fs::write(&output_file_name, remote_script_content) {
|
||||||
|
failure!(
|
||||||
|
"Write script: {} failed: {}",
|
||||||
|
output_file_name.to_string_lossy(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
success!("Write file: {} success", output_file_name.to_string_lossy());
|
||||||
|
match fs::set_permissions(&output_file_name, PermissionsExt::from_mode(0o755)) {
|
||||||
|
Err(e) => failure!(
|
||||||
|
"Chmod script: {} permission failed: {}",
|
||||||
|
output_file_name.to_string_lossy(),
|
||||||
|
e
|
||||||
|
),
|
||||||
|
Ok(_) => success!(
|
||||||
|
"Chmod script: {} permission succeed",
|
||||||
|
output_file_name.to_string_lossy()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/list.rs
Normal file
72
src/list.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
use crate::install::install_scripts;
|
||||||
|
use crate::util;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub fn list_scripts(
|
||||||
|
script_repo: &Option<String>,
|
||||||
|
filter: Option<&String>,
|
||||||
|
update_listed_scripts: bool,
|
||||||
|
) {
|
||||||
|
let script_meta_map = util::fetch_script_meta_or_die(script_repo);
|
||||||
|
|
||||||
|
let mut matched_need_update_scripts = vec![];
|
||||||
|
let mut messages = vec![];
|
||||||
|
for script_meta in script_meta_map.values() {
|
||||||
|
let script_name = &script_meta.script_name;
|
||||||
|
if let Some(filter) = filter {
|
||||||
|
if !script_name.contains(filter) {
|
||||||
|
// NOT CONTAINS `filter`, SKIP
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let user_home = util::get_user_home_or_die();
|
||||||
|
let full_script_path = PathBuf::from(&user_home).join("bin").join(script_name);
|
||||||
|
let is_script_exists = full_script_path.is_file();
|
||||||
|
let script_sha256 = if is_script_exists {
|
||||||
|
let script_content = fs::read(&full_script_path).unwrap_or(vec![]);
|
||||||
|
sha256::digest(&script_content)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
|
let is_script_matches = script_sha256 == script_meta.script_sha256;
|
||||||
|
let script_size = rust_util::util_size::get_display_size(script_meta.script_length as i64);
|
||||||
|
let padding_length = 40 - script_name.len();
|
||||||
|
messages.push(format!(
|
||||||
|
"- {} {} {} {}",
|
||||||
|
iff!(is_script_exists, iff!(is_script_matches, "✅", "✔️ "), "➖"),
|
||||||
|
script_name,
|
||||||
|
".".repeat(iff!(padding_length < 1, 1, padding_length)),
|
||||||
|
script_size,
|
||||||
|
));
|
||||||
|
if is_script_exists && !is_script_matches {
|
||||||
|
matched_need_update_scripts.push(script_meta.script_name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if filter.is_some() {
|
||||||
|
messages.insert(
|
||||||
|
0,
|
||||||
|
format!(
|
||||||
|
"Total {} script(s), found {} script(s):",
|
||||||
|
script_meta_map.len(),
|
||||||
|
messages.len()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
messages.insert(0, format!("Found {} script(s):", script_meta_map.len()));
|
||||||
|
}
|
||||||
|
success!("{}", messages.join("\n"));
|
||||||
|
|
||||||
|
if update_listed_scripts {
|
||||||
|
if matched_need_update_scripts.len() > 0 {
|
||||||
|
information!(
|
||||||
|
"Update listed scripts ON:\n- {}\n",
|
||||||
|
matched_need_update_scripts.join("\n- ")
|
||||||
|
);
|
||||||
|
install_scripts(script_repo, &matched_need_update_scripts);
|
||||||
|
} else {
|
||||||
|
information!("No update listed scripts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/main.rs
113
src/main.rs
@@ -1,50 +1,93 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rust_util;
|
extern crate rust_util;
|
||||||
|
|
||||||
use std::process::Command;
|
use argh::FromArgs;
|
||||||
use std::path::PathBuf;
|
use std::env;
|
||||||
|
|
||||||
|
mod help;
|
||||||
|
mod install;
|
||||||
|
mod list;
|
||||||
|
mod run_rs;
|
||||||
|
#[cfg(feature = "switch-ts-lang")]
|
||||||
|
mod run_ts;
|
||||||
|
mod template;
|
||||||
|
mod util;
|
||||||
|
mod verify;
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
/// Run script
|
||||||
|
struct RunScriptArgs {
|
||||||
|
/// help message
|
||||||
|
#[argh(switch, short = 'h')]
|
||||||
|
help_message: bool,
|
||||||
|
/// script template
|
||||||
|
#[argh(switch, short = 't')]
|
||||||
|
template: bool,
|
||||||
|
/// list scripts
|
||||||
|
#[argh(switch, short = 'l')]
|
||||||
|
list: bool,
|
||||||
|
/// install script
|
||||||
|
#[argh(switch, short = 'i')]
|
||||||
|
install: bool,
|
||||||
|
/// update listed scripts
|
||||||
|
#[argh(option)]
|
||||||
|
update_listed_scripts: Option<bool>,
|
||||||
|
/// script repo
|
||||||
|
#[argh(option, short = 'R')]
|
||||||
|
script_repo: Option<String>,
|
||||||
|
/// output
|
||||||
|
#[argh(option, short = 'o')]
|
||||||
|
output: Option<String>,
|
||||||
|
/// arguments
|
||||||
|
#[argh(positional, greedy)]
|
||||||
|
arguments: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let home = std::env::var("HOME").unwrap_or_else(|_|
|
let rs_args: RunScriptArgs = argh::from_env();
|
||||||
failure_and_exit!("$HOME not found!")
|
let cmd_version = env!("CARGO_PKG_VERSION");
|
||||||
|
if env::args().len() == 1 {
|
||||||
|
let cmd_name = util::get_cmd_name();
|
||||||
|
failure_and_exit!(
|
||||||
|
"{cmd_name} v{cmd_version}, need arguments, `{cmd_name} -h` or `{cmd_name} --help-message` for help"
|
||||||
);
|
);
|
||||||
let rust_script = PathBuf::from(format!("{}/.cargo/bin/rust-script", home));
|
|
||||||
if !rust_script.exists() {
|
|
||||||
warning!("rust-script not found, install it...");
|
|
||||||
let mut cargo_install_rust_script = Command::new("cargo");
|
|
||||||
cargo_install_rust_script.args(&["install", "rust-script"]);
|
|
||||||
debugging!("Run command: {:?}", cargo_install_rust_script);
|
|
||||||
let run_result = rust_util::util_cmd::run_command_and_wait(&mut cargo_install_rust_script);
|
|
||||||
if let Err(e) = run_result {
|
|
||||||
failure_and_exit!("Install rust-script failed: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut copied_args = vec![];
|
if rs_args.help_message {
|
||||||
std::env::args().skip(1).for_each(|arg| {
|
help::print_help();
|
||||||
copied_args.push(arg);
|
return;
|
||||||
});
|
}
|
||||||
debugging!("Arguments: {:?}", copied_args);
|
if rs_args.template {
|
||||||
|
template::print_template(&rs_args.output);
|
||||||
if copied_args.is_empty() {
|
return;
|
||||||
failure_and_exit!("Run `runrs --help` for help.");
|
}
|
||||||
|
if rs_args.list {
|
||||||
|
list::list_scripts(
|
||||||
|
&rs_args.script_repo,
|
||||||
|
rs_args.arguments.first(),
|
||||||
|
rs_args.update_listed_scripts.unwrap_or(false),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if rs_args.install {
|
||||||
|
install::install_scripts(&rs_args.script_repo, &rs_args.arguments);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(arg1) = copied_args.get(0) {
|
do_run_script(&rs_args);
|
||||||
match arg1.as_str() {
|
|
||||||
":::" => {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
_ => {} // IGNORE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut run_rs = Command::new(rust_script);
|
#[allow(unreachable_code)]
|
||||||
for arg in &copied_args {
|
fn do_run_script(rs_args: &RunScriptArgs) {
|
||||||
run_rs.arg(arg);
|
#[cfg(feature = "switch-rust-lang")]
|
||||||
|
{
|
||||||
|
run_rs::do_run_script(rs_args);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
debugging!("Run command: {:?}", run_rs);
|
#[cfg(feature = "switch-ts-lang")]
|
||||||
if let Err(e) = rust_util::util_cmd::run_command_and_wait(&mut run_rs) {
|
{
|
||||||
failure_and_exit!("Run rust-script failed: {}", e);
|
run_ts::do_run_script(&rs_args);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
failure_and_exit!("Only rust script supported.");
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/run_rs.rs
Normal file
43
src/run_rs.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use crate::{util, verify, RunScriptArgs};
|
||||||
|
use rust_util::util_env::is_env_on;
|
||||||
|
use rust_util::util_os::get_user_home;
|
||||||
|
|
||||||
|
pub fn do_run_script(args: &RunScriptArgs) {
|
||||||
|
if args.arguments.is_empty() {
|
||||||
|
failure_and_exit!("Must assign a script file name");
|
||||||
|
}
|
||||||
|
let script_file = &args.arguments[0];
|
||||||
|
verify::verify_script(script_file, is_env_on("RUNRS_SKIP_VERIFY"));
|
||||||
|
|
||||||
|
let (_, script_sha256) = util::read_file_and_digest(script_file);
|
||||||
|
debugging!("File {} -> sha256: {}", script_file, script_sha256);
|
||||||
|
|
||||||
|
let user_home = get_user_home().unwrap_or_else(|| failure_and_exit!("$HOME not found!"));
|
||||||
|
let rust_script = util::get_run_script_bin_name(&user_home);
|
||||||
|
let cache_script_bin_name = get_cache_script_bin_name(&user_home, &script_sha256);
|
||||||
|
let mut run_script_cmd = util::build_script_command(
|
||||||
|
rust_script,
|
||||||
|
script_file,
|
||||||
|
&script_sha256,
|
||||||
|
&cache_script_bin_name,
|
||||||
|
);
|
||||||
|
for arg in args.arguments.iter().skip(1) {
|
||||||
|
run_script_cmd.arg(arg);
|
||||||
|
}
|
||||||
|
util::run_script_command(script_file, &cache_script_bin_name, &mut run_script_cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cache_script_bin_name(user_home: &str, script_sha256: &str) -> String {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let cache_script_bin_name = format!(
|
||||||
|
"{}/Library/Caches/rust-script/binaries/release/{}",
|
||||||
|
user_home, script_sha256
|
||||||
|
);
|
||||||
|
// #[cfg(target_os = "linux")]
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
let cache_script_bin_name = format!(
|
||||||
|
"{}/.cache/rust-script/binaries/release/{}",
|
||||||
|
user_home, script_sha256
|
||||||
|
);
|
||||||
|
cache_script_bin_name
|
||||||
|
}
|
||||||
136
src/run_ts.rs
Normal file
136
src/run_ts.rs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
use crate::{verify, RunScriptArgs};
|
||||||
|
use rust_util::util_cmd;
|
||||||
|
use rust_util::util_env::is_env_on;
|
||||||
|
use std::fs;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
const RUNTIME_DENO: &str = "--runtime-deno";
|
||||||
|
const RUNTIME_BUN: &str = "--runtime-bun";
|
||||||
|
|
||||||
|
pub fn do_run_script(args: &RunScriptArgs) {
|
||||||
|
if args.arguments.is_empty() {
|
||||||
|
failure_and_exit!("Must assign a script file name");
|
||||||
|
}
|
||||||
|
debugging!("Run ts args: {:?}", args.arguments);
|
||||||
|
|
||||||
|
let mut is_runtime_deno = true;
|
||||||
|
let (script_file, first_arg) = (|| {
|
||||||
|
for (i, arg) in args.arguments.iter().enumerate() {
|
||||||
|
if arg == RUNTIME_DENO || arg == RUNTIME_BUN {
|
||||||
|
is_runtime_deno = arg == RUNTIME_DENO;
|
||||||
|
}
|
||||||
|
if !arg.starts_with("--") {
|
||||||
|
return (arg, i == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(&args.arguments[args.arguments.len() - 1], false)
|
||||||
|
})();
|
||||||
|
verify::verify_script(script_file, is_env_on("RUNTS_SKIP_VERIFY"));
|
||||||
|
|
||||||
|
let mut cmd = Command::new("/usr/bin/env");
|
||||||
|
// #!/usr/bin/env runts -- --allow-env
|
||||||
|
// #!/usr/bin/env runts -- [--runtime-deno | --runtime-bun] <args>
|
||||||
|
if first_arg {
|
||||||
|
let first_line_args = read_first_line_parse_args(script_file);
|
||||||
|
debugging!("Runts first line args: {:?}", first_line_args);
|
||||||
|
let left_first_line_args = first_line_args
|
||||||
|
.iter()
|
||||||
|
.skip_while(|arg| *arg != "--" && *arg != "run")
|
||||||
|
.skip(1)
|
||||||
|
.filter(|arg| {
|
||||||
|
if *arg == RUNTIME_DENO || *arg == RUNTIME_BUN {
|
||||||
|
is_runtime_deno = *arg == RUNTIME_DENO;
|
||||||
|
debugging!("Runts runtime arg: {}, is runtime deno: {}", *arg, is_runtime_deno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
cmd.args(["-S", iff!(is_runtime_deno, "deno", "bun"), "run"]);
|
||||||
|
cmd.args(left_first_line_args);
|
||||||
|
} else {
|
||||||
|
cmd.args(["-S", iff!(is_runtime_deno, "deno", "bun"), "run"]);
|
||||||
|
}
|
||||||
|
for arg in &args.arguments {
|
||||||
|
if arg == RUNTIME_DENO || arg == RUNTIME_BUN {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cmd.arg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
debugging!("Run command: {cmd:?}");
|
||||||
|
if let Err(e) = util_cmd::run_command_and_wait(&mut cmd) {
|
||||||
|
failure_and_exit!("Run deno: {script_file} failed: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_first_line_parse_args(script_file: &str) -> Vec<String> {
|
||||||
|
if let Ok(script_content) = fs::read_to_string(script_file) {
|
||||||
|
if let Some(first_line) = script_content.lines().next() {
|
||||||
|
if first_line.starts_with("#!") {
|
||||||
|
return parse_args_line(&first_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_args_line(line: &str) -> Vec<String> {
|
||||||
|
let mut args = vec![];
|
||||||
|
|
||||||
|
let mut ln = String::new();
|
||||||
|
let mut in_quota = false;
|
||||||
|
let mut single_quota = false;
|
||||||
|
let chars = line.chars().collect::<Vec<_>>();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < chars.len() {
|
||||||
|
let c = chars[i];
|
||||||
|
if in_quota {
|
||||||
|
if (single_quota && c == '\'') || (!single_quota && c == '"') {
|
||||||
|
in_quota = false;
|
||||||
|
} else if c == '\\' {
|
||||||
|
if i + 1 < chars.len() {
|
||||||
|
i += 1;
|
||||||
|
let nc = chars[i];
|
||||||
|
ln.push(nc);
|
||||||
|
} else {
|
||||||
|
ln.push(c);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ln.push(c);
|
||||||
|
}
|
||||||
|
} else if c == ' ' || c == '\t' {
|
||||||
|
if !ln.is_empty() {
|
||||||
|
args.push(ln.clone());
|
||||||
|
ln.clear();
|
||||||
|
}
|
||||||
|
} else if c == '\'' {
|
||||||
|
in_quota = true;
|
||||||
|
single_quota = true;
|
||||||
|
} else if c == '"' {
|
||||||
|
in_quota = true;
|
||||||
|
single_quota = false;
|
||||||
|
} else {
|
||||||
|
ln.push(c);
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
if !ln.is_empty() {
|
||||||
|
args.push(ln);
|
||||||
|
}
|
||||||
|
args
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_args_line() {
|
||||||
|
assert!(parse_args_line("").is_empty());
|
||||||
|
assert_eq!(vec!["a".to_string()], parse_args_line("a"));
|
||||||
|
assert_eq!(
|
||||||
|
vec!["a".to_string(), "aaaa'bbb".to_string()],
|
||||||
|
parse_args_line("a 'aaaa\\'bbb'")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
vec!["--allow-a".to_string(), "--allow-b".to_string()],
|
||||||
|
parse_args_line(" --allow-a --allow-b")
|
||||||
|
);
|
||||||
|
}
|
||||||
13
src/script.help.dart.txt
Normal file
13
src/script.help.dart.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
rundart v{CARGO_PKG_VERSION} - A Tool for Run dart Scripts
|
||||||
|
|
||||||
|
Show help:
|
||||||
|
$ rundart -h|--help
|
||||||
|
|
||||||
|
Show template:
|
||||||
|
$ rundart -t|--template [--output script.dart]
|
||||||
|
|
||||||
|
Show scriptions:
|
||||||
|
$ rundart -l|--list [filter] [--update-listed-scripts]
|
||||||
|
|
||||||
|
Install script:
|
||||||
|
$ rundart -i|--install <script.dart>
|
||||||
15
src/script.help.go.txt
Normal file
15
src/script.help.go.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
rungo v{CARGO_PKG_VERSION} - A Tool for Run Go Scripts
|
||||||
|
|
||||||
|
Show help:
|
||||||
|
$ rungo -h|--help
|
||||||
|
|
||||||
|
Show template:
|
||||||
|
$ rungo -t|--template [--output script.go]
|
||||||
|
|
||||||
|
Show scriptions:
|
||||||
|
$ rungo -l|--list [filter] [--update-listed-scripts]
|
||||||
|
|
||||||
|
Install script:
|
||||||
|
$ rungo -i|--install <script.go>
|
||||||
|
|
||||||
|
Powered by gorun, know more reference: https://github.com/erning/gorun
|
||||||
33
src/script.help.rs.txt
Normal file
33
src/script.help.rs.txt
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
runrs v{CARGO_PKG_VERSION} - {CARGO_PKG_DESCRIPTION}
|
||||||
|
|
||||||
|
Show help:
|
||||||
|
$ runrs -h|--help
|
||||||
|
|
||||||
|
Show template:
|
||||||
|
$ runrs -t|--template [--output script.rs]
|
||||||
|
|
||||||
|
Show scriptions:
|
||||||
|
$ runrs [-R|--script-repo runrs] -l|--list [filter] [--update-listed-scripts]
|
||||||
|
|
||||||
|
Install script:
|
||||||
|
$ runrs [-R|--script-repo runrs] -i|--install <script.rs>
|
||||||
|
|
||||||
|
Run script:
|
||||||
|
$ runrs <script.rs> [arguments]
|
||||||
|
|
||||||
|
Binaries dir:
|
||||||
|
~/Library/Caches/rust-script/binaries/release/
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
┌──────────────────────┬────────────────────────────────────────────────┐
|
||||||
|
│ Variable │ Description │
|
||||||
|
├──────────────────────┼────────────────────────────────────────────────┤
|
||||||
|
│ HOME │ User home, default from OS system │
|
||||||
|
│ RUNRS_SKIP_CACHE │ Skip compiled cached file, `bool` │
|
||||||
|
│ RUNRS_REBUILD │ Force rebuild, `bool` │
|
||||||
|
│ RUNRS_SILENT_BUILD │ Build new binary in silent mode, `bool` │
|
||||||
|
│ RUNRS_SKIP_VERIFY │ Skip script verification, `bool` │
|
||||||
|
│ RUNRS_RUST_SCRIPT │ `rust_script` command line bin file │
|
||||||
|
│ RUNRS_MAX_SCRIPT_LEN │ Max script length, default 1MB │
|
||||||
|
└──────────────────────┴────────────────────────────────────────────────┘
|
||||||
|
> Type `bool`: `true`, `yes`, `on` or `1` for true, otherwise false
|
||||||
20
src/script.help.ts.txt
Normal file
20
src/script.help.ts.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
runts v{CARGO_PKG_VERSION} - A Tool for Run TS Scripts
|
||||||
|
|
||||||
|
Show help:
|
||||||
|
$ runts -h|--help
|
||||||
|
|
||||||
|
Show template:
|
||||||
|
$ runts -t|--template [--output script.ts]
|
||||||
|
|
||||||
|
Show scriptions:
|
||||||
|
$ runts -l|--list [filter] [--update-listed-scripts]
|
||||||
|
|
||||||
|
Install script:
|
||||||
|
$ runts -i|--install <script.ts>
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
┌──────────────────────┬────────────────────────────────────────────────┐
|
||||||
|
│ Variable │ Description │
|
||||||
|
├──────────────────────┼────────────────────────────────────────────────┤
|
||||||
|
│ RUNTS_SKIP_VERIFY │ Skip script verification, `bool` │
|
||||||
|
└──────────────────────┴────────────────────────────────────────────────┘
|
||||||
5
src/script.template.dart
Normal file
5
src/script.template.dart
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env dart
|
||||||
|
|
||||||
|
main(List<String> args) {
|
||||||
|
print("Hello world.");
|
||||||
|
}
|
||||||
40
src/script.template.go
Normal file
40
src/script.template.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/// 2>/dev/null ; gorun "$0" "$@" ; exit $?
|
||||||
|
// OR: #!/usr/bin/env gorun
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const GetIpUrl = "https://hatter.ink/ip/ip.jsonp"
|
||||||
|
|
||||||
|
type IpResponse struct {
|
||||||
|
Status int16 `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
UserAgent string `json:"userAgent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
response, err := http.Get(GetIpUrl)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[ERROR] Get IP failed: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[ERROR] Get IP failed: %s\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
var ipReponse IpResponse
|
||||||
|
if err := json.Unmarshal(body, &ipReponse); err != nil {
|
||||||
|
fmt.Printf("[ERROR] Get IP failed: %s\n", err)
|
||||||
|
os.Exit(3)
|
||||||
|
}
|
||||||
|
fmt.Printf("[INFO] Your IP address: %s\n", ipReponse.IP)
|
||||||
|
}
|
||||||
32
src/script.template.rs
Normal file
32
src/script.template.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env runrs
|
||||||
|
|
||||||
|
//! ```cargo
|
||||||
|
//! [dependencies]
|
||||||
|
//! serde = { version = "1.0", features = ["derive"] }
|
||||||
|
//! serde_json = "1.0"
|
||||||
|
//! reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
|
//! rust_util = { version = "0.6" }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use rust_util::{failure_and_exit, success};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct IpResponse {
|
||||||
|
status: u16,
|
||||||
|
message: String,
|
||||||
|
ip: String,
|
||||||
|
#[serde(rename = "userAgent")]
|
||||||
|
user_agent: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const GET_IP_URL: &'static str = "https://hatter.ink/ip/ip.jsonp";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let ip_response: IpResponse = reqwest::blocking::get(GET_IP_URL).unwrap_or_else(|e| {
|
||||||
|
failure_and_exit!("Send request to: {} failed: {}", GET_IP_URL, e);
|
||||||
|
}).json().unwrap_or_else(|e| {
|
||||||
|
failure_and_exit!("Parse response from: {}, failed: {}", GET_IP_URL, e);
|
||||||
|
});
|
||||||
|
success!("Your IP address is: {}", ip_response.ip);
|
||||||
|
}
|
||||||
3
src/script.template.ts
Normal file
3
src/script.template.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env runts -- --allow-env
|
||||||
|
|
||||||
|
console.log("Hello world.");
|
||||||
33
src/template.rs
Normal file
33
src/template.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
pub fn print_template(output: &Option<String>) {
|
||||||
|
let script = get_script_template();
|
||||||
|
if let Some(output_file) = output {
|
||||||
|
if fs::metadata(output_file).is_ok() {
|
||||||
|
failure!("Output script file exists: {}", output_file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Err(e) = fs::write(output_file, script) {
|
||||||
|
failure!("Write script file: {}, failed: {}", output_file, e);
|
||||||
|
} else {
|
||||||
|
success!("Write script file success: {}", output_file);
|
||||||
|
let _ = fs::set_permissions(output_file, PermissionsExt::from_mode(0o755));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("{}", script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
fn get_script_template() -> &'static str {
|
||||||
|
#[cfg(feature = "switch-rust-lang")]
|
||||||
|
return include_str!("script.template.rs");
|
||||||
|
#[cfg(feature = "switch-go-lang")]
|
||||||
|
return include_str!("script.template.go");
|
||||||
|
#[cfg(feature = "switch-ts-lang")]
|
||||||
|
return include_str!("script.template.ts");
|
||||||
|
#[cfg(feature = "switch-dart-lang")]
|
||||||
|
return include_str!("script.template.dart");
|
||||||
|
panic!("Unknown feature assigned.");
|
||||||
|
}
|
||||||
216
src/util.rs
Normal file
216
src/util.rs
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
use rust_util::util_cmd;
|
||||||
|
use rust_util::util_env::is_env_on;
|
||||||
|
use rust_util::util_os::get_user_home;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
use std::{env, fs, process};
|
||||||
|
|
||||||
|
#[cfg(feature = "switch-rust-lang")]
|
||||||
|
const RS_SCRIPT_META_URL: &str =
|
||||||
|
"https://git.hatter.ink/rust-scripts/scriptbase/raw/branch/main/script-meta-v2.json";
|
||||||
|
#[cfg(feature = "switch-go-lang")]
|
||||||
|
const GO_SCRIPT_META_URL: &str =
|
||||||
|
"https://git.hatter.ink/hatter/go-scripts/raw/branch/main/script-meta-v2.json";
|
||||||
|
#[cfg(feature = "switch-ts-lang")]
|
||||||
|
const TS_SCRIPT_META_URL: &str =
|
||||||
|
"https://git.hatter.ink/hatter/ts-scripts/raw/branch/main/script-meta-v2.json";
|
||||||
|
#[cfg(feature = "switch-dart-lang")]
|
||||||
|
const DART_SCRIPT_META_URL: &str =
|
||||||
|
"https://git.hatter.ink/hatter/dart-scripts/raw/branch/main/script-meta-v2.json";
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ScriptMeta {
|
||||||
|
pub script_name: String,
|
||||||
|
pub script_length: u64,
|
||||||
|
pub script_sha256: String,
|
||||||
|
pub script_full_url: String,
|
||||||
|
pub single_script_file: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
pub fn get_script_meta_url(script_repo: &Option<String>) -> String {
|
||||||
|
if let Some(script_repo) = script_repo {
|
||||||
|
return format!("https://hatter.ink/script/{script_repo}/script-meta-v2.action");
|
||||||
|
}
|
||||||
|
#[cfg(feature = "switch-rust-lang")]
|
||||||
|
return RS_SCRIPT_META_URL.to_string();
|
||||||
|
#[cfg(feature = "switch-go-lang")]
|
||||||
|
return GO_SCRIPT_META_URL.to_string();
|
||||||
|
#[cfg(feature = "switch-ts-lang")]
|
||||||
|
return TS_SCRIPT_META_URL.to_string();
|
||||||
|
#[cfg(feature = "switch-dart-lang")]
|
||||||
|
return DART_SCRIPT_META_URL.to_string();
|
||||||
|
panic!("Unknown feature assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
pub fn get_cmd_name() -> &'static str {
|
||||||
|
#[cfg(feature = "switch-rust-lang")]
|
||||||
|
return "runrs";
|
||||||
|
#[cfg(feature = "switch-go-lang")]
|
||||||
|
return "rungo";
|
||||||
|
#[cfg(feature = "switch-ts-lang")]
|
||||||
|
return "runts";
|
||||||
|
#[cfg(feature = "switch-dart-lang")]
|
||||||
|
return "rundart";
|
||||||
|
panic!("Unknown feature assigned.");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_script_meta_or_die(script_repo: &Option<String>) -> BTreeMap<String, ScriptMeta> {
|
||||||
|
let file_meta_url = get_script_meta_url(script_repo);
|
||||||
|
debugging!("Loading URL: {}", file_meta_url);
|
||||||
|
let response = reqwest::blocking::get(file_meta_url).unwrap_or_else(|e| {
|
||||||
|
failure_and_exit!("Get file-meta.json failed: {}", e);
|
||||||
|
});
|
||||||
|
debugging!("Load response: {}", response.status().as_u16());
|
||||||
|
if response.status().as_u16() != 200 {
|
||||||
|
failure_and_exit!(
|
||||||
|
"Get file-meta.json failed, status: {}",
|
||||||
|
response.status().as_u16()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let text = response.text().unwrap_or_else(|e| {
|
||||||
|
failure_and_exit!("Get file-meta.json failed: {}", e);
|
||||||
|
});
|
||||||
|
debugging!("Response text: {}", &text);
|
||||||
|
|
||||||
|
let script_meta_map: BTreeMap<String, ScriptMeta> =
|
||||||
|
serde_json::from_str(&text).expect("Parse script-meta.json failed.");
|
||||||
|
script_meta_map
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_user_home_or_die() -> String {
|
||||||
|
get_user_home().expect("Get user home failed!")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_script_command(
|
||||||
|
rust_script: PathBuf,
|
||||||
|
script_file: &str,
|
||||||
|
script_sha256: &str,
|
||||||
|
cache_script_bin_name: &str,
|
||||||
|
) -> Command {
|
||||||
|
let skip_cache = is_env_on("RUNRS_SKIP_CACHE");
|
||||||
|
let rebuild = is_env_on("RUNRS_REBUILD");
|
||||||
|
let cache_script_bin_name_exists = fs::metadata(cache_script_bin_name).is_ok();
|
||||||
|
debugging!(
|
||||||
|
"Bin name: {} {}exists",
|
||||||
|
cache_script_bin_name,
|
||||||
|
iff!(cache_script_bin_name_exists, "", "not ")
|
||||||
|
);
|
||||||
|
if !rebuild && !skip_cache && cache_script_bin_name_exists {
|
||||||
|
Command::new(cache_script_bin_name)
|
||||||
|
} else {
|
||||||
|
let mut cmd = Command::new(rust_script);
|
||||||
|
if !is_env_on("RUNRS_SILENT_BUILD") {
|
||||||
|
information!("Cached binary not exists, RUNRS_SILENT_BUILD=1 turn off");
|
||||||
|
cmd.arg("--cargo-output");
|
||||||
|
}
|
||||||
|
if rebuild {
|
||||||
|
cmd.arg("--force");
|
||||||
|
}
|
||||||
|
cmd.args(["--bin-name", script_sha256, script_file]);
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_script_command(
|
||||||
|
script_file: &str,
|
||||||
|
cache_script_bin_name: &str,
|
||||||
|
run_script_cmd: &mut Command,
|
||||||
|
) -> ! {
|
||||||
|
debugging!("Run command: {:?}", run_script_cmd);
|
||||||
|
let run_command_start = SystemTime::now();
|
||||||
|
match util_cmd::run_command_and_wait(run_script_cmd) {
|
||||||
|
Err(e) => failure_and_exit!("Run rust-script failed: {}", e),
|
||||||
|
Ok(exit_status) => {
|
||||||
|
write_script_file_to_src(script_file, cache_script_bin_name);
|
||||||
|
if let Ok(run_command_cost) = SystemTime::now().duration_since(run_command_start) {
|
||||||
|
debugging!("Run command cost: {}ms", run_command_cost.as_millis());
|
||||||
|
}
|
||||||
|
process::exit(exit_status.code().unwrap_or(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_script_file_to_src(script_file: &str, cache_script_bin_name: &str) {
|
||||||
|
if let Ok(Some(canonicalized_script_file)) = PathBuf::from(script_file)
|
||||||
|
.canonicalize()
|
||||||
|
.map(|f| f.to_str().map(|f| f.to_string()))
|
||||||
|
{
|
||||||
|
let cache_script_bin_name_src = format!("{}.src", cache_script_bin_name);
|
||||||
|
let src_content = fs::read_to_string(&cache_script_bin_name_src).ok();
|
||||||
|
if src_content
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| c.contains(&canonicalized_script_file))
|
||||||
|
.unwrap_or_else(|| false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let new_src_content = src_content.unwrap_or_else(|| "".to_string())
|
||||||
|
+ &format!("{}\n", canonicalized_script_file);
|
||||||
|
if fs::write(&cache_script_bin_name_src, &new_src_content).is_ok() {
|
||||||
|
debugging!(
|
||||||
|
"Add {} to {}",
|
||||||
|
canonicalized_script_file,
|
||||||
|
cache_script_bin_name_src
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_run_script_bin_name(home: &str) -> PathBuf {
|
||||||
|
if let Ok(rust_script) = env::var("RUNRS_RUST_SCRIPT") {
|
||||||
|
let rust_script_path_buf = PathBuf::from(&rust_script);
|
||||||
|
if !rust_script_path_buf.exists() {
|
||||||
|
warning!("RUST_SCRIPT={} not exists", &rust_script);
|
||||||
|
}
|
||||||
|
return rust_script_path_buf;
|
||||||
|
}
|
||||||
|
let rust_script = PathBuf::from(format!("{}/.cargo/bin/rust-script", home));
|
||||||
|
if !rust_script.exists() {
|
||||||
|
warning!("rust-script not found, install it...");
|
||||||
|
let mut cargo_install_rust_script = Command::new("cargo");
|
||||||
|
cargo_install_rust_script.args([
|
||||||
|
"install",
|
||||||
|
"--git",
|
||||||
|
"https://git.hatter.ink/hatter/runrs",
|
||||||
|
"rust-script",
|
||||||
|
]);
|
||||||
|
debugging!("Run command: {:?}", cargo_install_rust_script);
|
||||||
|
match util_cmd::run_command_and_wait(&mut cargo_install_rust_script) {
|
||||||
|
Err(e) => failure_and_exit!("Install rust-script failed: {}", e),
|
||||||
|
Ok(exist_status) => {
|
||||||
|
if !exist_status.success() {
|
||||||
|
failure!("Install rust-script not success: {}", exist_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rust_script
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_file_and_digest(script_file: &str) -> (String, String) {
|
||||||
|
let default_max_script_len = 1024 * 1024;
|
||||||
|
let max_script_len: u64 = env::var("RUNRS_MAX_SCRIPT_LEN")
|
||||||
|
.map(|len| len.parse().unwrap_or(default_max_script_len))
|
||||||
|
.unwrap_or(default_max_script_len);
|
||||||
|
match fs::metadata(script_file) {
|
||||||
|
Err(_) => failure_and_exit!("Script file not exists: {}", script_file),
|
||||||
|
Ok(metadata) => {
|
||||||
|
if metadata.is_dir() {
|
||||||
|
failure_and_exit!("Assigned input is dir: {}", script_file);
|
||||||
|
} else if metadata.len() > max_script_len {
|
||||||
|
failure_and_exit!("Script file: {} too large: {}", script_file, metadata.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let script_content = fs::read_to_string(script_file)
|
||||||
|
.unwrap_or_else(|e| failure_and_exit!("Read file: {}, failed: {}", script_file, e));
|
||||||
|
let script_sha256 = sha256::digest(script_content.as_str());
|
||||||
|
|
||||||
|
(script_content, script_sha256)
|
||||||
|
}
|
||||||
20
src/verify.rs
Normal file
20
src/verify.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
use script_sign::Script;
|
||||||
|
|
||||||
|
pub fn verify_script(file: &str, skip_verify: bool) {
|
||||||
|
if skip_verify {
|
||||||
|
debugging!("Script {file} verification skipped");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match Script::verify_script_file_with_system_key_map(file) {
|
||||||
|
Ok(true) => {
|
||||||
|
// Verify file ok!
|
||||||
|
debugging!("Script {file} verification success");
|
||||||
|
}
|
||||||
|
Ok(false) => {
|
||||||
|
failure_and_exit!("Verify script {file} failed, no signature or bad signature");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
failure_and_exit!("Verify script {file} failed, error: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user