Ghostty: run commands directly via base64 to avoid Gatekeeper prompts; translate README; anonymize examples

This commit is contained in:
SW 2026-06-15 12:47:51 +02:00
parent 44ebb4054b
commit 1da2553b92
8 changed files with 79 additions and 76 deletions

1
Cargo.lock generated
View file

@ -759,6 +759,7 @@ dependencies = [
name = "lines" name = "lines"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64",
"clap", "clap",
"crossterm", "crossterm",
"directories", "directories",

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
base64 = "0.22.1"
clap = { version = "4.6.1", features = ["derive"] } clap = { version = "4.6.1", features = ["derive"] }
crossterm = "0.29.0" crossterm = "0.29.0"
directories = "6.0.0" directories = "6.0.0"

View file

@ -1,79 +1,79 @@
# lines # lines
TUI aplikace pro rychlé spouštění často používaných příkazů — ideální pro SSH skripty, aktualizace a cokoliv, co pouštíš pravidelně z terminálu. A TUI app for quickly running frequently used commands — ideal for SSH scripts, updates, and anything you run regularly from the terminal.
## Instalace ## Installation
```bash ```bash
brew install janvete/tools/lines brew install janvete/tools/lines
``` ```
## Použití ## Usage
```bash ```bash
lines # otevře TUI lines # opens the TUI
lines --dir /cesta/k/datům # použije jinou datovou složku lines --dir /path # use a different data directory
``` ```
## Struktura dat ## Data structure
Data jsou běžné `.md` soubory ve složce `~/.lines`. Skupiny = podsložky. Data is stored as plain `.md` files in `~/.lines`. Groups are subdirectories.
``` ```
~/.lines ~/.lines
├── Synology ├── Servers
│ └── dsmview.md │ └── web.md
└── Scripts └── Scripts
└── update.md └── update.md
``` ```
### Konfigurace ### `.md` format
Vytvoř soubor `~/.lines/config.toml`: Each section starts with `# Command name`. Command lines follow the heading.
```toml
terminal = "Ghostty" # možnosti: Terminal, Ghostty, iTerm
```
Výchozí terminál je `Terminal` (macOS Terminál.app).
## Formát `.md`
Každá sekce začíná nadpisem `# Název příkazu`. Pod ním následují řádky příkazů.
```markdown ```markdown
# Sysel DSM # Web server
dsmview ssh root@192.168.40.15 ssh admin@web.example.com
# Johy DSM # Database
dsmview ssh root@192.168.51.3 ssh admin@db.example.com
``` ```
Sekce bez příkazů znamená "spustit všechny příkazy v tomto souboru". A section without command lines means "run all commands in this file".
```markdown ```markdown
# Spustí vše # Run all
# Docker update # Docker update
ssh root@192.168.53.3 /opt/mealie/update.sh ssh admin@host1.example.com /opt/app/update.sh
ssh root@192.168.53.5 /opt/yamtrack/update.sh ssh admin@host2.example.com /opt/app/update.sh
``` ```
## Ovládání ## Configuration
| Klávesa | Akce | Create `~/.lines/config.toml`:
|---------|------|
| `↑/↓` nebo `j/k` | pohyb v seznamu |
| `←/→` nebo `h/l` | přepínání panelů |
| `Enter` | spustí vybraný příkaz / všechny příkazy |
| `e` | otevře aktuální `.md` soubor v `$EDITOR` |
| `r` | znovu načte data ze složky |
| `q` nebo `Esc` | ukončí aplikaci |
## Zálohování ```toml
terminal = "Ghostty" # options: Terminal, Ghostty, iTerm
```
Celá datová složka `~/.lines` je obyčejný text — stačí ji zkopírovat na disk, do Gitu nebo iCloudu. Na novém Macu ji přeneseš a `lines` ji rovnou načte. The default terminal is `Terminal` (macOS Terminal.app).
## Licence ## Controls
| Key | Action |
|-----|--------|
| `↑/↓` or `j/k` | move |
| `←/→` or `h/l` | switch panel |
| `Enter` | run selected command / all commands |
| `e` | open current `.md` file in `$EDITOR` |
| `r` | reload data |
| `q` / `Esc` | quit |
## Backup
The entire `~/.lines` folder is plain text. Copy it to a disk, Git repo, or iCloud. On a new Mac, move it to the same location and `lines` works immediately.
## License
MIT MIT

View file

@ -1,5 +1,5 @@
# Spustí vše # Run all
# Docker update # Docker update
ssh root@192.168.53.3 /opt/mealie/update.sh ssh admin@host1.example.com /opt/app/update.sh
ssh root@192.168.53.5 /opt/yamtrack/update.sh ssh admin@host2.example.com /opt/app/update.sh

5
examples/Servers/web.md Normal file
View file

@ -0,0 +1,5 @@
# Main server
ssh admin@web.example.com
# Backup server
ssh admin@backup.example.com

View file

@ -1,5 +0,0 @@
# Sysel DSM
dsmview ssh root@192.168.40.15
# Johy DSM
dsmview ssh root@192.168.51.3

View file

@ -1,9 +1,11 @@
use std::fs; use std::fs;
use std::io::Write; use std::io::Write;
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf}; use std::path::Path;
use std::process::Command; use std::process::Command;
use base64::{engine::general_purpose::STANDARD, Engine};
use crate::config::Terminal; use crate::config::Terminal;
use crate::parser::{CommandFile, Section}; use crate::parser::{CommandFile, Section};
@ -22,20 +24,15 @@ pub fn run_section(section: &Section, file: &CommandFile, terminal: &Terminal, d
return; return;
} }
let script = build_script(&commands); match terminal {
execute_in_terminal(&script, terminal, data_dir); Terminal::Ghostty => run_in_ghostty(&commands),
_ => run_via_script(&commands, terminal, data_dir),
}
} }
fn build_script(commands: &[String]) -> String { fn run_via_script(commands: &[String], terminal: &Terminal, data_dir: &Path) {
commands.join("\n") let script = commands.join("\n");
} let run_dir = data_dir.join(".run");
fn script_dir(data_dir: &Path) -> PathBuf {
data_dir.join(".run")
}
fn execute_in_terminal(script: &str, terminal: &Terminal, data_dir: &Path) {
let run_dir = script_dir(data_dir);
if fs::create_dir_all(&run_dir).is_err() { if fs::create_dir_all(&run_dir).is_err() {
return; return;
} }
@ -60,12 +57,28 @@ fn execute_in_terminal(script: &str, terminal: &Terminal, data_dir: &Path) {
let _ = fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755)); let _ = fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755));
match terminal { match terminal {
Terminal::Ghostty => run_in_ghostty(&script_path),
Terminal::Iterm => run_in_iterm(&script_path), Terminal::Iterm => run_in_iterm(&script_path),
Terminal::Builtin => run_in_terminal_app(&script_path), Terminal::Builtin => run_in_terminal_app(&script_path),
Terminal::Ghostty => unreachable!(),
} }
} }
fn run_in_ghostty(commands: &[String]) {
let script = commands.join("\n");
let encoded = STANDARD.encode(script);
let shell_command = format!("echo {} | base64 -D | bash", encoded);
let _ = Command::new("open")
.arg("-na")
.arg("Ghostty")
.arg("--args")
.arg("-e")
.arg("bash")
.arg("-c")
.arg(shell_command)
.spawn();
}
fn run_in_terminal_app(script_path: &Path) { fn run_in_terminal_app(script_path: &Path) {
let path_str = script_path.to_string_lossy(); let path_str = script_path.to_string_lossy();
let applescript = format!( let applescript = format!(
@ -82,18 +95,6 @@ end tell"#,
.spawn(); .spawn();
} }
fn run_in_ghostty(script_path: &Path) {
let path_str = script_path.to_string_lossy();
let _ = Command::new("open")
.arg("-na")
.arg("Ghostty")
.arg("--args")
.arg("-e")
.arg("bash")
.arg(path_str.as_ref())
.spawn();
}
fn run_in_iterm(script_path: &Path) { fn run_in_iterm(script_path: &Path) {
let path_str = script_path.to_string_lossy(); let path_str = script_path.to_string_lossy();
let applescript = format!( let applescript = format!(

View file

@ -3,6 +3,6 @@ use std::path::PathBuf;
#[test] #[test]
fn test_example_files_exist() { fn test_example_files_exist() {
let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples"); let examples = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples");
assert!(examples.join("Synology/dsmview.md").exists()); assert!(examples.join("Servers/web.md").exists());
assert!(examples.join("Scripts/update.md").exists()); assert!(examples.join("Scripts/update.md").exists());
} }