rrun-cli 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rrun_cli-0.1.1/PKG-INFO +175 -0
- rrun_cli-0.1.1/README.md +150 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/pyproject.toml +5 -4
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun/__init__.py +1 -1
- rrun_cli-0.1.1/src/rrun_cli.egg-info/PKG-INFO +175 -0
- rrun_cli-0.1.0/PKG-INFO +0 -134
- rrun_cli-0.1.0/README.md +0 -110
- rrun_cli-0.1.0/src/rrun_cli.egg-info/PKG-INFO +0 -134
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/LICENSE +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/setup.cfg +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun/__main__.py +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun/executor.py +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun/registry.py +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun/remote-requirements.txt +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun/setup.py +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun_cli.egg-info/SOURCES.txt +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun_cli.egg-info/dependency_links.txt +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun_cli.egg-info/entry_points.txt +0 -0
- {rrun_cli-0.1.0 → rrun_cli-0.1.1}/src/rrun_cli.egg-info/top_level.txt +0 -0
rrun_cli-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rrun-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Run local scripts on remote machines over SSH via stdin pipes — no escaping/encoding hell. Supports python/powershell/bash, with a unified remote Python 3.12 venv provisioner.
|
|
5
|
+
Author: waqiju
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/waqiju/rrun
|
|
8
|
+
Project-URL: Repository, https://github.com/waqiju/rrun
|
|
9
|
+
Project-URL: Issues, https://github.com/waqiju/rrun/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/waqiju/rrun/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Documentation, https://github.com/waqiju/rrun/blob/main/docs/remote-exec-conventions.md
|
|
12
|
+
Keywords: ssh,remote-exec,powershell,windows,ops,automation
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: System Administrators
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Operating System :: MacOS
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Topic :: System :: Systems Administration
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# rrun
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/rrun-cli/)
|
|
29
|
+
[](https://pypi.org/project/rrun-cli/)
|
|
30
|
+
[](https://github.com/waqiju/rrun/blob/main/LICENSE)
|
|
31
|
+
[](https://github.com/waqiju/rrun/actions/workflows/publish.yml)
|
|
32
|
+
|
|
33
|
+
**[中文文档](README.zh-CN.md)**
|
|
34
|
+
|
|
35
|
+
Run local scripts on remote machines over SSH — **via stdin pipes, never via command-line arguments** — so quoting, escaping, and CJK/UTF-8 encoding survive the `bash → ssh → cmd/powershell` journey intact.
|
|
36
|
+
|
|
37
|
+
- python / powershell / bash, inferred from the file extension or the remote OS
|
|
38
|
+
- Remote stdout/stderr stream back verbatim; the **exit code passes through**, so pipes and CI just work
|
|
39
|
+
- ssh ControlMaster multiplexing: ~0.5s on first contact, ~0.02s afterwards
|
|
40
|
+
- One-command provisioning of a unified **remote Python 3.12 venv** (`rrun setup`) — the remote never touches the internet
|
|
41
|
+
- Zero third-party Python dependencies
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pipx install rrun-cli # provides the `rrun` command (plus a `remote-machine` alias)
|
|
47
|
+
# or: pip install rrun-cli
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> The PyPI distribution is named `rrun-cli` (`rrun` sits on PyPI's prohibited-name list as it
|
|
51
|
+
> is confusable with `run`); the installed command is plain `rrun`.
|
|
52
|
+
|
|
53
|
+
**Control machine:** Linux or macOS (Windows works via WSL), Python ≥ 3.10, plus `ssh` and `sshpass`:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
sudo apt install sshpass # Debian/Ubuntu
|
|
57
|
+
brew install hudochenkov/sshpass/sshpass # macOS (sshpass is not in homebrew-core)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Remote machines:** an OpenSSH server with password auth enabled. Windows remotes execute via PowerShell, Mac/Linux remotes via bash.
|
|
61
|
+
|
|
62
|
+
## Quickstart
|
|
63
|
+
|
|
64
|
+
Create `~/.rrun/machines.json` (full format in [Configuration](#configuration-machinesjson)):
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"machines": [
|
|
69
|
+
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows", "user": "admin", "password": "secret" }
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
rrun machines # verify the inventory is picked up (redacted)
|
|
78
|
+
printf 'Write-Output "hello 中文"\n' > demo.ps1
|
|
79
|
+
rrun exec my-win-box demo.ps1 # powershell, inferred from .ps1
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
That's the whole loop: write a script locally → it runs remotely → you get its output and exit code.
|
|
83
|
+
|
|
84
|
+
## Why
|
|
85
|
+
|
|
86
|
+
Running commands on remote Windows machines from a POSIX shell is a minefield: sshd lands you in `cmd` with a GBK codepage, quotes and `$` get eaten by one of the three shells along the way, and any non-ASCII argument gets mojibake'd. rrun's rules:
|
|
87
|
+
|
|
88
|
+
- Script content (UTF-8, CJK welcome) always travels through **stdin** — never the command line.
|
|
89
|
+
- PowerShell payloads are base64-wrapped into a **single-line pure-ASCII wrapper** (`powershell -Command -` executes stdin line-by-line; multi-line input fails silently), then decoded and invoked as a ScriptBlock remotely.
|
|
90
|
+
- Command-line arguments are restricted to ASCII and passed through safely (`sys.argv` / `$@` / `$args`). Put anything fancier in the script itself or in a JSON file.
|
|
91
|
+
|
|
92
|
+
The full set of hard-won conventions and internals: [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md) ([中文](docs/remote-exec-conventions.zh-CN.md)).
|
|
93
|
+
|
|
94
|
+
## Subcommands
|
|
95
|
+
|
|
96
|
+
| Command | Purpose |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `rrun exec <host> <script\|-c ...>` | Execute a local script / inline content remotely |
|
|
99
|
+
| `rrun machines [--json]` | List merged machines (redacted, with source) |
|
|
100
|
+
| `rrun config` | Diagnose the machines.json source chain |
|
|
101
|
+
| `rrun setup <host\|--all> [--force]` | Provision the unified remote Python 3.12 venv (idempotent) |
|
|
102
|
+
| `rrun pip <host> -- list` | Run pip inside the remote unified venv |
|
|
103
|
+
| `rrun close [<host>\|--all]` | Close ssh ControlMaster multiplexed connections |
|
|
104
|
+
|
|
105
|
+
Useful `exec` flags: `--lang bash|powershell|python`, `--workdir`, `--env K=V`, `--timeout`, `--python <path>` (skip detection), `--no-mux`, `-q`.
|
|
106
|
+
|
|
107
|
+
### Exit codes
|
|
108
|
+
|
|
109
|
+
| Code | Meaning |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `0`–`254` | The remote script's own exit code, passed through unchanged |
|
|
112
|
+
| `255` | ssh transport failure (unreachable / auth failure / connection dropped) |
|
|
113
|
+
| `124` | local `--timeout` expired; the local ssh client was killed |
|
|
114
|
+
|
|
115
|
+
## Configuration: machines.json
|
|
116
|
+
|
|
117
|
+
Credentials live in local `machines.json` files — see [machines.template.json](machines.template.json):
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"defaults": { "windows": { "os": "Windows" } },
|
|
122
|
+
"machines": [
|
|
123
|
+
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows",
|
|
124
|
+
"user": "admin", "password": "secret" }
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Sources are merged by machine name, highest priority first (all optional, failures skipped silently):
|
|
130
|
+
|
|
131
|
+
1. `$RRUN_CONFIG` (os.pathsep-separated, multiple files allowed)
|
|
132
|
+
2. `$REMOTE_MACHINE_CONFIG` (legacy name, still honored)
|
|
133
|
+
3. `./machines.json` (current working directory)
|
|
134
|
+
4. `~/.rrun/machines.json`
|
|
135
|
+
5. `~/.rrun/machines.d/*.json` (sorted by filename — point each inventory at its own file/symlink)
|
|
136
|
+
6. Legacy: `~/.remote-machine/machines.json` (POSIX) / `C:\tools\remote-machine\machines.json` (Windows)
|
|
137
|
+
|
|
138
|
+
Per-file `defaults` sections (keyed by `os`, lowercased) are applied before merging. Inspect the chain with `rrun config`; list machines (redacted) with `rrun machines`.
|
|
139
|
+
|
|
140
|
+
## Remote Python: unified 3.12 environment
|
|
141
|
+
|
|
142
|
+
For `python` scripts, rrun requires a **3.12.x** interpreter on the remote, probed in order:
|
|
143
|
+
|
|
144
|
+
1. Unified venv: `C:\tools\remote-machine\venv\Scripts\python.exe` / `~/.remote-machine/venv/bin/python`
|
|
145
|
+
2. Standalone base: `...\python312\python.exe` / `~/.remote-machine/python312/bin/python3`
|
|
146
|
+
3. Existing installs: `C:\Python\Python312\python.exe` / `python3.12` on PATH
|
|
147
|
+
|
|
148
|
+
If none match, run `rrun setup <host>`. It is idempotent and non-destructive: if the machine lacks Python 3.12, a [python-build-standalone](https://github.com/astral-sh/python-build-standalone) tarball is downloaded to the local cache (`~/.cache/rrun/`) and streamed over ssh stdin — no registry, no PATH changes, no admin rights, and the remote never touches GitHub. The venv gets its own `pip.ini`/`pip.conf` (global pip config untouched) plus the standard packages from the bundled `remote-requirements.txt` (override with `~/.rrun/remote-requirements.txt`).
|
|
149
|
+
|
|
150
|
+
## Security
|
|
151
|
+
|
|
152
|
+
- `machines.json` stores **plaintext passwords**. Keep it local, `chmod 600`, never commit it.
|
|
153
|
+
- rrun authenticates with passwords via `sshpass`; **key-based authentication is not supported yet** (on the roadmap).
|
|
154
|
+
- The audit log never records passwords, and `--env` values are logged as keys only.
|
|
155
|
+
|
|
156
|
+
## Auditing & state
|
|
157
|
+
|
|
158
|
+
- Every execution appends a JSON line to `~/.rrun/log/remote-exec.jsonl` (host, lang, script sha1, exit code, duration).
|
|
159
|
+
- Inline `-c` payloads are archived under `~/.rrun/drops/` for replay.
|
|
160
|
+
- The state root defaults to `~/.rrun/` and is overridable via `$RRUN_HOME`.
|
|
161
|
+
|
|
162
|
+
## Upgrading / uninstalling
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pipx upgrade rrun-cli
|
|
166
|
+
pipx uninstall rrun-cli
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Contributing
|
|
170
|
+
|
|
171
|
+
Issues and PRs are welcome. Development setup: clone → `python3.12 -m venv .venv && .venv/bin/pip install -e .` → hack → smoke-test with `rrun machines`. Releases are cut by pushing a `vX.Y.Z` tag; CI builds and publishes to PyPI via trusted publishing. See [CHANGELOG.md](CHANGELOG.md).
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
[MIT](LICENSE)
|
rrun_cli-0.1.1/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# rrun
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/rrun-cli/)
|
|
4
|
+
[](https://pypi.org/project/rrun-cli/)
|
|
5
|
+
[](https://github.com/waqiju/rrun/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/waqiju/rrun/actions/workflows/publish.yml)
|
|
7
|
+
|
|
8
|
+
**[中文文档](README.zh-CN.md)**
|
|
9
|
+
|
|
10
|
+
Run local scripts on remote machines over SSH — **via stdin pipes, never via command-line arguments** — so quoting, escaping, and CJK/UTF-8 encoding survive the `bash → ssh → cmd/powershell` journey intact.
|
|
11
|
+
|
|
12
|
+
- python / powershell / bash, inferred from the file extension or the remote OS
|
|
13
|
+
- Remote stdout/stderr stream back verbatim; the **exit code passes through**, so pipes and CI just work
|
|
14
|
+
- ssh ControlMaster multiplexing: ~0.5s on first contact, ~0.02s afterwards
|
|
15
|
+
- One-command provisioning of a unified **remote Python 3.12 venv** (`rrun setup`) — the remote never touches the internet
|
|
16
|
+
- Zero third-party Python dependencies
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pipx install rrun-cli # provides the `rrun` command (plus a `remote-machine` alias)
|
|
22
|
+
# or: pip install rrun-cli
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> The PyPI distribution is named `rrun-cli` (`rrun` sits on PyPI's prohibited-name list as it
|
|
26
|
+
> is confusable with `run`); the installed command is plain `rrun`.
|
|
27
|
+
|
|
28
|
+
**Control machine:** Linux or macOS (Windows works via WSL), Python ≥ 3.10, plus `ssh` and `sshpass`:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
sudo apt install sshpass # Debian/Ubuntu
|
|
32
|
+
brew install hudochenkov/sshpass/sshpass # macOS (sshpass is not in homebrew-core)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Remote machines:** an OpenSSH server with password auth enabled. Windows remotes execute via PowerShell, Mac/Linux remotes via bash.
|
|
36
|
+
|
|
37
|
+
## Quickstart
|
|
38
|
+
|
|
39
|
+
Create `~/.rrun/machines.json` (full format in [Configuration](#configuration-machinesjson)):
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"machines": [
|
|
44
|
+
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows", "user": "admin", "password": "secret" }
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
rrun machines # verify the inventory is picked up (redacted)
|
|
53
|
+
printf 'Write-Output "hello 中文"\n' > demo.ps1
|
|
54
|
+
rrun exec my-win-box demo.ps1 # powershell, inferred from .ps1
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
That's the whole loop: write a script locally → it runs remotely → you get its output and exit code.
|
|
58
|
+
|
|
59
|
+
## Why
|
|
60
|
+
|
|
61
|
+
Running commands on remote Windows machines from a POSIX shell is a minefield: sshd lands you in `cmd` with a GBK codepage, quotes and `$` get eaten by one of the three shells along the way, and any non-ASCII argument gets mojibake'd. rrun's rules:
|
|
62
|
+
|
|
63
|
+
- Script content (UTF-8, CJK welcome) always travels through **stdin** — never the command line.
|
|
64
|
+
- PowerShell payloads are base64-wrapped into a **single-line pure-ASCII wrapper** (`powershell -Command -` executes stdin line-by-line; multi-line input fails silently), then decoded and invoked as a ScriptBlock remotely.
|
|
65
|
+
- Command-line arguments are restricted to ASCII and passed through safely (`sys.argv` / `$@` / `$args`). Put anything fancier in the script itself or in a JSON file.
|
|
66
|
+
|
|
67
|
+
The full set of hard-won conventions and internals: [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md) ([中文](docs/remote-exec-conventions.zh-CN.md)).
|
|
68
|
+
|
|
69
|
+
## Subcommands
|
|
70
|
+
|
|
71
|
+
| Command | Purpose |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `rrun exec <host> <script\|-c ...>` | Execute a local script / inline content remotely |
|
|
74
|
+
| `rrun machines [--json]` | List merged machines (redacted, with source) |
|
|
75
|
+
| `rrun config` | Diagnose the machines.json source chain |
|
|
76
|
+
| `rrun setup <host\|--all> [--force]` | Provision the unified remote Python 3.12 venv (idempotent) |
|
|
77
|
+
| `rrun pip <host> -- list` | Run pip inside the remote unified venv |
|
|
78
|
+
| `rrun close [<host>\|--all]` | Close ssh ControlMaster multiplexed connections |
|
|
79
|
+
|
|
80
|
+
Useful `exec` flags: `--lang bash|powershell|python`, `--workdir`, `--env K=V`, `--timeout`, `--python <path>` (skip detection), `--no-mux`, `-q`.
|
|
81
|
+
|
|
82
|
+
### Exit codes
|
|
83
|
+
|
|
84
|
+
| Code | Meaning |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `0`–`254` | The remote script's own exit code, passed through unchanged |
|
|
87
|
+
| `255` | ssh transport failure (unreachable / auth failure / connection dropped) |
|
|
88
|
+
| `124` | local `--timeout` expired; the local ssh client was killed |
|
|
89
|
+
|
|
90
|
+
## Configuration: machines.json
|
|
91
|
+
|
|
92
|
+
Credentials live in local `machines.json` files — see [machines.template.json](machines.template.json):
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"defaults": { "windows": { "os": "Windows" } },
|
|
97
|
+
"machines": [
|
|
98
|
+
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows",
|
|
99
|
+
"user": "admin", "password": "secret" }
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Sources are merged by machine name, highest priority first (all optional, failures skipped silently):
|
|
105
|
+
|
|
106
|
+
1. `$RRUN_CONFIG` (os.pathsep-separated, multiple files allowed)
|
|
107
|
+
2. `$REMOTE_MACHINE_CONFIG` (legacy name, still honored)
|
|
108
|
+
3. `./machines.json` (current working directory)
|
|
109
|
+
4. `~/.rrun/machines.json`
|
|
110
|
+
5. `~/.rrun/machines.d/*.json` (sorted by filename — point each inventory at its own file/symlink)
|
|
111
|
+
6. Legacy: `~/.remote-machine/machines.json` (POSIX) / `C:\tools\remote-machine\machines.json` (Windows)
|
|
112
|
+
|
|
113
|
+
Per-file `defaults` sections (keyed by `os`, lowercased) are applied before merging. Inspect the chain with `rrun config`; list machines (redacted) with `rrun machines`.
|
|
114
|
+
|
|
115
|
+
## Remote Python: unified 3.12 environment
|
|
116
|
+
|
|
117
|
+
For `python` scripts, rrun requires a **3.12.x** interpreter on the remote, probed in order:
|
|
118
|
+
|
|
119
|
+
1. Unified venv: `C:\tools\remote-machine\venv\Scripts\python.exe` / `~/.remote-machine/venv/bin/python`
|
|
120
|
+
2. Standalone base: `...\python312\python.exe` / `~/.remote-machine/python312/bin/python3`
|
|
121
|
+
3. Existing installs: `C:\Python\Python312\python.exe` / `python3.12` on PATH
|
|
122
|
+
|
|
123
|
+
If none match, run `rrun setup <host>`. It is idempotent and non-destructive: if the machine lacks Python 3.12, a [python-build-standalone](https://github.com/astral-sh/python-build-standalone) tarball is downloaded to the local cache (`~/.cache/rrun/`) and streamed over ssh stdin — no registry, no PATH changes, no admin rights, and the remote never touches GitHub. The venv gets its own `pip.ini`/`pip.conf` (global pip config untouched) plus the standard packages from the bundled `remote-requirements.txt` (override with `~/.rrun/remote-requirements.txt`).
|
|
124
|
+
|
|
125
|
+
## Security
|
|
126
|
+
|
|
127
|
+
- `machines.json` stores **plaintext passwords**. Keep it local, `chmod 600`, never commit it.
|
|
128
|
+
- rrun authenticates with passwords via `sshpass`; **key-based authentication is not supported yet** (on the roadmap).
|
|
129
|
+
- The audit log never records passwords, and `--env` values are logged as keys only.
|
|
130
|
+
|
|
131
|
+
## Auditing & state
|
|
132
|
+
|
|
133
|
+
- Every execution appends a JSON line to `~/.rrun/log/remote-exec.jsonl` (host, lang, script sha1, exit code, duration).
|
|
134
|
+
- Inline `-c` payloads are archived under `~/.rrun/drops/` for replay.
|
|
135
|
+
- The state root defaults to `~/.rrun/` and is overridable via `$RRUN_HOME`.
|
|
136
|
+
|
|
137
|
+
## Upgrading / uninstalling
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pipx upgrade rrun-cli
|
|
141
|
+
pipx uninstall rrun-cli
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Contributing
|
|
145
|
+
|
|
146
|
+
Issues and PRs are welcome. Development setup: clone → `python3.12 -m venv .venv && .venv/bin/pip install -e .` → hack → smoke-test with `rrun machines`. Releases are cut by pushing a `vX.Y.Z` tag; CI builds and publishes to PyPI via trusted publishing. See [CHANGELOG.md](CHANGELOG.md).
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
[MIT](LICENSE)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rrun-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.1"
|
|
8
8
|
description = "Run local scripts on remote machines over SSH via stdin pipes — no escaping/encoding hell. Supports python/powershell/bash, with a unified remote Python 3.12 venv provisioner."
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
license =
|
|
10
|
+
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
authors = [{ name = "waqiju" }]
|
|
13
13
|
keywords = ["ssh", "remote-exec", "powershell", "windows", "ops", "automation"]
|
|
@@ -16,7 +16,6 @@ classifiers = [
|
|
|
16
16
|
"Environment :: Console",
|
|
17
17
|
"Intended Audience :: Developers",
|
|
18
18
|
"Intended Audience :: System Administrators",
|
|
19
|
-
"License :: OSI Approved :: MIT License",
|
|
20
19
|
"Operating System :: POSIX :: Linux",
|
|
21
20
|
"Operating System :: MacOS",
|
|
22
21
|
"Programming Language :: Python :: 3",
|
|
@@ -29,6 +28,8 @@ dependencies = []
|
|
|
29
28
|
Homepage = "https://github.com/waqiju/rrun"
|
|
30
29
|
Repository = "https://github.com/waqiju/rrun"
|
|
31
30
|
Issues = "https://github.com/waqiju/rrun/issues"
|
|
31
|
+
Changelog = "https://github.com/waqiju/rrun/blob/main/CHANGELOG.md"
|
|
32
|
+
Documentation = "https://github.com/waqiju/rrun/blob/main/docs/remote-exec-conventions.md"
|
|
32
33
|
|
|
33
34
|
[project.scripts]
|
|
34
35
|
rrun = "rrun.__main__:main"
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rrun-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Run local scripts on remote machines over SSH via stdin pipes — no escaping/encoding hell. Supports python/powershell/bash, with a unified remote Python 3.12 venv provisioner.
|
|
5
|
+
Author: waqiju
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/waqiju/rrun
|
|
8
|
+
Project-URL: Repository, https://github.com/waqiju/rrun
|
|
9
|
+
Project-URL: Issues, https://github.com/waqiju/rrun/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/waqiju/rrun/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Documentation, https://github.com/waqiju/rrun/blob/main/docs/remote-exec-conventions.md
|
|
12
|
+
Keywords: ssh,remote-exec,powershell,windows,ops,automation
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: System Administrators
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Operating System :: MacOS
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Topic :: System :: Systems Administration
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# rrun
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/rrun-cli/)
|
|
29
|
+
[](https://pypi.org/project/rrun-cli/)
|
|
30
|
+
[](https://github.com/waqiju/rrun/blob/main/LICENSE)
|
|
31
|
+
[](https://github.com/waqiju/rrun/actions/workflows/publish.yml)
|
|
32
|
+
|
|
33
|
+
**[中文文档](README.zh-CN.md)**
|
|
34
|
+
|
|
35
|
+
Run local scripts on remote machines over SSH — **via stdin pipes, never via command-line arguments** — so quoting, escaping, and CJK/UTF-8 encoding survive the `bash → ssh → cmd/powershell` journey intact.
|
|
36
|
+
|
|
37
|
+
- python / powershell / bash, inferred from the file extension or the remote OS
|
|
38
|
+
- Remote stdout/stderr stream back verbatim; the **exit code passes through**, so pipes and CI just work
|
|
39
|
+
- ssh ControlMaster multiplexing: ~0.5s on first contact, ~0.02s afterwards
|
|
40
|
+
- One-command provisioning of a unified **remote Python 3.12 venv** (`rrun setup`) — the remote never touches the internet
|
|
41
|
+
- Zero third-party Python dependencies
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pipx install rrun-cli # provides the `rrun` command (plus a `remote-machine` alias)
|
|
47
|
+
# or: pip install rrun-cli
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> The PyPI distribution is named `rrun-cli` (`rrun` sits on PyPI's prohibited-name list as it
|
|
51
|
+
> is confusable with `run`); the installed command is plain `rrun`.
|
|
52
|
+
|
|
53
|
+
**Control machine:** Linux or macOS (Windows works via WSL), Python ≥ 3.10, plus `ssh` and `sshpass`:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
sudo apt install sshpass # Debian/Ubuntu
|
|
57
|
+
brew install hudochenkov/sshpass/sshpass # macOS (sshpass is not in homebrew-core)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Remote machines:** an OpenSSH server with password auth enabled. Windows remotes execute via PowerShell, Mac/Linux remotes via bash.
|
|
61
|
+
|
|
62
|
+
## Quickstart
|
|
63
|
+
|
|
64
|
+
Create `~/.rrun/machines.json` (full format in [Configuration](#configuration-machinesjson)):
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"machines": [
|
|
69
|
+
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows", "user": "admin", "password": "secret" }
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
rrun machines # verify the inventory is picked up (redacted)
|
|
78
|
+
printf 'Write-Output "hello 中文"\n' > demo.ps1
|
|
79
|
+
rrun exec my-win-box demo.ps1 # powershell, inferred from .ps1
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
That's the whole loop: write a script locally → it runs remotely → you get its output and exit code.
|
|
83
|
+
|
|
84
|
+
## Why
|
|
85
|
+
|
|
86
|
+
Running commands on remote Windows machines from a POSIX shell is a minefield: sshd lands you in `cmd` with a GBK codepage, quotes and `$` get eaten by one of the three shells along the way, and any non-ASCII argument gets mojibake'd. rrun's rules:
|
|
87
|
+
|
|
88
|
+
- Script content (UTF-8, CJK welcome) always travels through **stdin** — never the command line.
|
|
89
|
+
- PowerShell payloads are base64-wrapped into a **single-line pure-ASCII wrapper** (`powershell -Command -` executes stdin line-by-line; multi-line input fails silently), then decoded and invoked as a ScriptBlock remotely.
|
|
90
|
+
- Command-line arguments are restricted to ASCII and passed through safely (`sys.argv` / `$@` / `$args`). Put anything fancier in the script itself or in a JSON file.
|
|
91
|
+
|
|
92
|
+
The full set of hard-won conventions and internals: [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md) ([中文](docs/remote-exec-conventions.zh-CN.md)).
|
|
93
|
+
|
|
94
|
+
## Subcommands
|
|
95
|
+
|
|
96
|
+
| Command | Purpose |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `rrun exec <host> <script\|-c ...>` | Execute a local script / inline content remotely |
|
|
99
|
+
| `rrun machines [--json]` | List merged machines (redacted, with source) |
|
|
100
|
+
| `rrun config` | Diagnose the machines.json source chain |
|
|
101
|
+
| `rrun setup <host\|--all> [--force]` | Provision the unified remote Python 3.12 venv (idempotent) |
|
|
102
|
+
| `rrun pip <host> -- list` | Run pip inside the remote unified venv |
|
|
103
|
+
| `rrun close [<host>\|--all]` | Close ssh ControlMaster multiplexed connections |
|
|
104
|
+
|
|
105
|
+
Useful `exec` flags: `--lang bash|powershell|python`, `--workdir`, `--env K=V`, `--timeout`, `--python <path>` (skip detection), `--no-mux`, `-q`.
|
|
106
|
+
|
|
107
|
+
### Exit codes
|
|
108
|
+
|
|
109
|
+
| Code | Meaning |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `0`–`254` | The remote script's own exit code, passed through unchanged |
|
|
112
|
+
| `255` | ssh transport failure (unreachable / auth failure / connection dropped) |
|
|
113
|
+
| `124` | local `--timeout` expired; the local ssh client was killed |
|
|
114
|
+
|
|
115
|
+
## Configuration: machines.json
|
|
116
|
+
|
|
117
|
+
Credentials live in local `machines.json` files — see [machines.template.json](machines.template.json):
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"defaults": { "windows": { "os": "Windows" } },
|
|
122
|
+
"machines": [
|
|
123
|
+
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows",
|
|
124
|
+
"user": "admin", "password": "secret" }
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Sources are merged by machine name, highest priority first (all optional, failures skipped silently):
|
|
130
|
+
|
|
131
|
+
1. `$RRUN_CONFIG` (os.pathsep-separated, multiple files allowed)
|
|
132
|
+
2. `$REMOTE_MACHINE_CONFIG` (legacy name, still honored)
|
|
133
|
+
3. `./machines.json` (current working directory)
|
|
134
|
+
4. `~/.rrun/machines.json`
|
|
135
|
+
5. `~/.rrun/machines.d/*.json` (sorted by filename — point each inventory at its own file/symlink)
|
|
136
|
+
6. Legacy: `~/.remote-machine/machines.json` (POSIX) / `C:\tools\remote-machine\machines.json` (Windows)
|
|
137
|
+
|
|
138
|
+
Per-file `defaults` sections (keyed by `os`, lowercased) are applied before merging. Inspect the chain with `rrun config`; list machines (redacted) with `rrun machines`.
|
|
139
|
+
|
|
140
|
+
## Remote Python: unified 3.12 environment
|
|
141
|
+
|
|
142
|
+
For `python` scripts, rrun requires a **3.12.x** interpreter on the remote, probed in order:
|
|
143
|
+
|
|
144
|
+
1. Unified venv: `C:\tools\remote-machine\venv\Scripts\python.exe` / `~/.remote-machine/venv/bin/python`
|
|
145
|
+
2. Standalone base: `...\python312\python.exe` / `~/.remote-machine/python312/bin/python3`
|
|
146
|
+
3. Existing installs: `C:\Python\Python312\python.exe` / `python3.12` on PATH
|
|
147
|
+
|
|
148
|
+
If none match, run `rrun setup <host>`. It is idempotent and non-destructive: if the machine lacks Python 3.12, a [python-build-standalone](https://github.com/astral-sh/python-build-standalone) tarball is downloaded to the local cache (`~/.cache/rrun/`) and streamed over ssh stdin — no registry, no PATH changes, no admin rights, and the remote never touches GitHub. The venv gets its own `pip.ini`/`pip.conf` (global pip config untouched) plus the standard packages from the bundled `remote-requirements.txt` (override with `~/.rrun/remote-requirements.txt`).
|
|
149
|
+
|
|
150
|
+
## Security
|
|
151
|
+
|
|
152
|
+
- `machines.json` stores **plaintext passwords**. Keep it local, `chmod 600`, never commit it.
|
|
153
|
+
- rrun authenticates with passwords via `sshpass`; **key-based authentication is not supported yet** (on the roadmap).
|
|
154
|
+
- The audit log never records passwords, and `--env` values are logged as keys only.
|
|
155
|
+
|
|
156
|
+
## Auditing & state
|
|
157
|
+
|
|
158
|
+
- Every execution appends a JSON line to `~/.rrun/log/remote-exec.jsonl` (host, lang, script sha1, exit code, duration).
|
|
159
|
+
- Inline `-c` payloads are archived under `~/.rrun/drops/` for replay.
|
|
160
|
+
- The state root defaults to `~/.rrun/` and is overridable via `$RRUN_HOME`.
|
|
161
|
+
|
|
162
|
+
## Upgrading / uninstalling
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pipx upgrade rrun-cli
|
|
166
|
+
pipx uninstall rrun-cli
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Contributing
|
|
170
|
+
|
|
171
|
+
Issues and PRs are welcome. Development setup: clone → `python3.12 -m venv .venv && .venv/bin/pip install -e .` → hack → smoke-test with `rrun machines`. Releases are cut by pushing a `vX.Y.Z` tag; CI builds and publishes to PyPI via trusted publishing. See [CHANGELOG.md](CHANGELOG.md).
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
[MIT](LICENSE)
|
rrun_cli-0.1.0/PKG-INFO
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: rrun-cli
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Run local scripts on remote machines over SSH via stdin pipes — no escaping/encoding hell. Supports python/powershell/bash, with a unified remote Python 3.12 venv provisioner.
|
|
5
|
-
Author: waqiju
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/waqiju/rrun
|
|
8
|
-
Project-URL: Repository, https://github.com/waqiju/rrun
|
|
9
|
-
Project-URL: Issues, https://github.com/waqiju/rrun/issues
|
|
10
|
-
Keywords: ssh,remote-exec,powershell,windows,ops,automation
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: Intended Audience :: System Administrators
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
-
Classifier: Operating System :: MacOS
|
|
18
|
-
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Topic :: System :: Systems Administration
|
|
20
|
-
Requires-Python: >=3.10
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Dynamic: license-file
|
|
24
|
-
|
|
25
|
-
# rrun
|
|
26
|
-
|
|
27
|
-
Run local scripts on remote machines over SSH — **via stdin pipes, never via command-line arguments** — so quoting, escaping, and CJK/UTF-8 encoding survive the `bash → ssh → cmd/powershell` journey intact.
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
rrun exec my-win-box ./deploy.ps1 # powershell (inferred from .ps1)
|
|
31
|
-
rrun exec my-mac ./build.sh # bash
|
|
32
|
-
rrun exec my-win-box ./report.py # python (remote 3.12, -X utf8)
|
|
33
|
-
rrun exec my-win-box -c "Get-Date" # inline content (archived to ~/.rrun/drops/)
|
|
34
|
-
rrun exec my-mac ./etl.py -- arg1 arg2 # ASCII args pass-through
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Remote stdout → your stdout, remote stderr → your stderr, and the **exit code is passed through** unchanged (255 = ssh transport failure, 124 = local `--timeout`). Pipes, `&&`, and CI integration just work.
|
|
38
|
-
|
|
39
|
-
## Why
|
|
40
|
-
|
|
41
|
-
Running commands on remote Windows machines from a POSIX shell is a minefield: sshd lands you in `cmd` with a GBK codepage, quotes and `$` get eaten by one of three shells along the way, and any non-ASCII argument gets mojibake'd. rrun's rules:
|
|
42
|
-
|
|
43
|
-
- Script content (UTF-8, Chinese welcome) is always piped through **stdin** — never the command line.
|
|
44
|
-
- For PowerShell, the payload is base64-wrapped into a **single-line pure-ASCII wrapper** (`powershell -Command -` executes stdin line-by-line, multi-line fails silently), decoded and invoked as a ScriptBlock remotely.
|
|
45
|
-
- Command-line arguments are restricted to ASCII and passed through safely (`sys.argv` / `$@` / `$args`). Put anything fancier in the script or a JSON file.
|
|
46
|
-
|
|
47
|
-
See [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md) (中文) for the full set of hard-won conventions.
|
|
48
|
-
|
|
49
|
-
## Install
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
pipx install rrun-cli # recommended: isolated global CLI (provides the `rrun` command)
|
|
53
|
-
# or: pip install rrun-cli
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
> The PyPI distribution is named `rrun-cli` (plain `rrun` is on PyPI's prohibited-name list
|
|
57
|
-
> as it's confusable with `run`), but the installed command is `rrun` — plus the legacy alias
|
|
58
|
-
> `remote-machine`.
|
|
59
|
-
|
|
60
|
-
Both `rrun` and the legacy alias `remote-machine` are installed.
|
|
61
|
-
|
|
62
|
-
**Control machine requirements:** `ssh` + `sshpass` (`sudo apt install sshpass`), Python ≥ 3.10. Zero third-party Python dependencies.
|
|
63
|
-
**Remote machines:** OpenSSH server. Windows remotes execute via PowerShell; Mac/Linux via bash.
|
|
64
|
-
|
|
65
|
-
## Configuration: machines.json
|
|
66
|
-
|
|
67
|
-
Credentials live in a local `machines.json` (never committed — it's a local file, `chmod 600` recommended). See [machines.template.json](machines.template.json):
|
|
68
|
-
|
|
69
|
-
```json
|
|
70
|
-
{
|
|
71
|
-
"defaults": { "windows": { "os": "Windows" } },
|
|
72
|
-
"machines": [
|
|
73
|
-
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows",
|
|
74
|
-
"user": "admin", "password": "secret" }
|
|
75
|
-
]
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Sources are merged by machine name, highest priority first (all optional, failures skipped silently):
|
|
80
|
-
|
|
81
|
-
1. `$RRUN_CONFIG` (os.pathsep-separated, multiple files allowed)
|
|
82
|
-
2. `$REMOTE_MACHINE_CONFIG` (legacy name, still honored)
|
|
83
|
-
3. `./machines.json` (current working directory)
|
|
84
|
-
4. `~/.rrun/machines.json`
|
|
85
|
-
5. `~/.rrun/machines.d/*.json` (sorted by filename — point each inventory at its own file/symlink)
|
|
86
|
-
6. Legacy: `~/.remote-machine/machines.json` (POSIX) / `C:\tools\remote-machine\machines.json` (Windows)
|
|
87
|
-
|
|
88
|
-
Per-file `defaults` sections (keyed by `os`, lowercased) are applied before merging. Inspect the chain with `rrun config`; list machines (redacted) with `rrun machines`.
|
|
89
|
-
|
|
90
|
-
## Subcommands
|
|
91
|
-
|
|
92
|
-
| Command | Purpose |
|
|
93
|
-
|---|---|
|
|
94
|
-
| `rrun exec <host> <script\|-c ...>` | Execute a local script / inline content remotely |
|
|
95
|
-
| `rrun machines [--json]` | List merged machines (redacted, with source) |
|
|
96
|
-
| `rrun config` | Diagnose the machines.json source chain |
|
|
97
|
-
| `rrun setup <host\|--all> [--force]` | Provision the unified remote Python 3.12 venv (idempotent) |
|
|
98
|
-
| `rrun pip <host> -- list` | Run pip inside the remote unified venv |
|
|
99
|
-
| `rrun close [<host>\|--all]` | Close ssh ControlMaster multiplexed connections |
|
|
100
|
-
|
|
101
|
-
Useful `exec` flags: `--lang bash|powershell|python`, `--workdir`, `--env K=V`, `--timeout`, `--python <path>` (skip detection), `--no-mux`, `-q`.
|
|
102
|
-
|
|
103
|
-
## Remote Python: unified 3.12 environment
|
|
104
|
-
|
|
105
|
-
For `python` scripts, rrun requires a **3.12.x** interpreter on the remote, probed in order:
|
|
106
|
-
|
|
107
|
-
1. Unified venv: `C:\tools\remote-machine\venv\Scripts\python.exe` / `~/.remote-machine/venv/bin/python`
|
|
108
|
-
2. Standalone base: `...\python312\python.exe` / `~/.remote-machine/python312/bin/python3`
|
|
109
|
-
3. Existing installs: `C:\Python\Python312\python.exe` / `python3.12` on PATH
|
|
110
|
-
|
|
111
|
-
If none match, run `rrun setup <host>`. It is idempotent and non-destructive: if the machine lacks Python 3.12, a [python-build-standalone](https://github.com/astral-sh/python-build-standalone) tarball is downloaded to the local cache (`~/.cache/rrun/`) and streamed over ssh stdin — no registry, no PATH changes, no admin rights, and the remote never touches GitHub. The venv gets an Alibaba Cloud pip mirror in its own `pip.ini`/`pip.conf` (global config untouched) plus the standard packages from `remote-requirements.txt` (override with `~/.rrun/remote-requirements.txt`).
|
|
112
|
-
|
|
113
|
-
## Auditing & state
|
|
114
|
-
|
|
115
|
-
- Every execution appends a JSON line to `~/.rrun/log/remote-exec.jsonl` (host, lang, script sha1, exit code, duration — never passwords; env vars recorded as keys only).
|
|
116
|
-
- Inline `-c` payloads are archived under `~/.rrun/drops/` for replay.
|
|
117
|
-
- Root directory overridable via `$RRUN_HOME`.
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## 中文简介
|
|
122
|
-
|
|
123
|
-
rrun 解决「从 POSIX shell 在远程机器(尤其 Windows)执行脚本」的转码/转义地狱:
|
|
124
|
-
脚本内容一律走 **stdin 管道**(UTF-8,中文随便用),命令行参数只放行 ASCII;
|
|
125
|
-
PowerShell 载荷编码为单行纯 ASCII wrapper,退出码原样透传。
|
|
126
|
-
|
|
127
|
-
- 安装:`pipx install rrun-cli`(控制端需 `ssh` + `sshpass`;装好后命令是 `rrun`)
|
|
128
|
-
- 机器清单 `machines.json` 来源链:`$RRUN_CONFIG` → `./machines.json` → `~/.rrun/machines.json` → `~/.rrun/machines.d/*.json` → 旧位置兼容
|
|
129
|
-
- 远程 python 基线锁 3.12,`rrun setup <host>` 一键幂等初始化统一 venv(standalone 基座经 ssh 推流安装,远端无需访问 GitHub,pip 走阿里云镜像)
|
|
130
|
-
- 更多踩坑约定见 [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md)
|
|
131
|
-
|
|
132
|
-
## License
|
|
133
|
-
|
|
134
|
-
MIT
|
rrun_cli-0.1.0/README.md
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
# rrun
|
|
2
|
-
|
|
3
|
-
Run local scripts on remote machines over SSH — **via stdin pipes, never via command-line arguments** — so quoting, escaping, and CJK/UTF-8 encoding survive the `bash → ssh → cmd/powershell` journey intact.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
rrun exec my-win-box ./deploy.ps1 # powershell (inferred from .ps1)
|
|
7
|
-
rrun exec my-mac ./build.sh # bash
|
|
8
|
-
rrun exec my-win-box ./report.py # python (remote 3.12, -X utf8)
|
|
9
|
-
rrun exec my-win-box -c "Get-Date" # inline content (archived to ~/.rrun/drops/)
|
|
10
|
-
rrun exec my-mac ./etl.py -- arg1 arg2 # ASCII args pass-through
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Remote stdout → your stdout, remote stderr → your stderr, and the **exit code is passed through** unchanged (255 = ssh transport failure, 124 = local `--timeout`). Pipes, `&&`, and CI integration just work.
|
|
14
|
-
|
|
15
|
-
## Why
|
|
16
|
-
|
|
17
|
-
Running commands on remote Windows machines from a POSIX shell is a minefield: sshd lands you in `cmd` with a GBK codepage, quotes and `$` get eaten by one of three shells along the way, and any non-ASCII argument gets mojibake'd. rrun's rules:
|
|
18
|
-
|
|
19
|
-
- Script content (UTF-8, Chinese welcome) is always piped through **stdin** — never the command line.
|
|
20
|
-
- For PowerShell, the payload is base64-wrapped into a **single-line pure-ASCII wrapper** (`powershell -Command -` executes stdin line-by-line, multi-line fails silently), decoded and invoked as a ScriptBlock remotely.
|
|
21
|
-
- Command-line arguments are restricted to ASCII and passed through safely (`sys.argv` / `$@` / `$args`). Put anything fancier in the script or a JSON file.
|
|
22
|
-
|
|
23
|
-
See [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md) (中文) for the full set of hard-won conventions.
|
|
24
|
-
|
|
25
|
-
## Install
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pipx install rrun-cli # recommended: isolated global CLI (provides the `rrun` command)
|
|
29
|
-
# or: pip install rrun-cli
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
> The PyPI distribution is named `rrun-cli` (plain `rrun` is on PyPI's prohibited-name list
|
|
33
|
-
> as it's confusable with `run`), but the installed command is `rrun` — plus the legacy alias
|
|
34
|
-
> `remote-machine`.
|
|
35
|
-
|
|
36
|
-
Both `rrun` and the legacy alias `remote-machine` are installed.
|
|
37
|
-
|
|
38
|
-
**Control machine requirements:** `ssh` + `sshpass` (`sudo apt install sshpass`), Python ≥ 3.10. Zero third-party Python dependencies.
|
|
39
|
-
**Remote machines:** OpenSSH server. Windows remotes execute via PowerShell; Mac/Linux via bash.
|
|
40
|
-
|
|
41
|
-
## Configuration: machines.json
|
|
42
|
-
|
|
43
|
-
Credentials live in a local `machines.json` (never committed — it's a local file, `chmod 600` recommended). See [machines.template.json](machines.template.json):
|
|
44
|
-
|
|
45
|
-
```json
|
|
46
|
-
{
|
|
47
|
-
"defaults": { "windows": { "os": "Windows" } },
|
|
48
|
-
"machines": [
|
|
49
|
-
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows",
|
|
50
|
-
"user": "admin", "password": "secret" }
|
|
51
|
-
]
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Sources are merged by machine name, highest priority first (all optional, failures skipped silently):
|
|
56
|
-
|
|
57
|
-
1. `$RRUN_CONFIG` (os.pathsep-separated, multiple files allowed)
|
|
58
|
-
2. `$REMOTE_MACHINE_CONFIG` (legacy name, still honored)
|
|
59
|
-
3. `./machines.json` (current working directory)
|
|
60
|
-
4. `~/.rrun/machines.json`
|
|
61
|
-
5. `~/.rrun/machines.d/*.json` (sorted by filename — point each inventory at its own file/symlink)
|
|
62
|
-
6. Legacy: `~/.remote-machine/machines.json` (POSIX) / `C:\tools\remote-machine\machines.json` (Windows)
|
|
63
|
-
|
|
64
|
-
Per-file `defaults` sections (keyed by `os`, lowercased) are applied before merging. Inspect the chain with `rrun config`; list machines (redacted) with `rrun machines`.
|
|
65
|
-
|
|
66
|
-
## Subcommands
|
|
67
|
-
|
|
68
|
-
| Command | Purpose |
|
|
69
|
-
|---|---|
|
|
70
|
-
| `rrun exec <host> <script\|-c ...>` | Execute a local script / inline content remotely |
|
|
71
|
-
| `rrun machines [--json]` | List merged machines (redacted, with source) |
|
|
72
|
-
| `rrun config` | Diagnose the machines.json source chain |
|
|
73
|
-
| `rrun setup <host\|--all> [--force]` | Provision the unified remote Python 3.12 venv (idempotent) |
|
|
74
|
-
| `rrun pip <host> -- list` | Run pip inside the remote unified venv |
|
|
75
|
-
| `rrun close [<host>\|--all]` | Close ssh ControlMaster multiplexed connections |
|
|
76
|
-
|
|
77
|
-
Useful `exec` flags: `--lang bash|powershell|python`, `--workdir`, `--env K=V`, `--timeout`, `--python <path>` (skip detection), `--no-mux`, `-q`.
|
|
78
|
-
|
|
79
|
-
## Remote Python: unified 3.12 environment
|
|
80
|
-
|
|
81
|
-
For `python` scripts, rrun requires a **3.12.x** interpreter on the remote, probed in order:
|
|
82
|
-
|
|
83
|
-
1. Unified venv: `C:\tools\remote-machine\venv\Scripts\python.exe` / `~/.remote-machine/venv/bin/python`
|
|
84
|
-
2. Standalone base: `...\python312\python.exe` / `~/.remote-machine/python312/bin/python3`
|
|
85
|
-
3. Existing installs: `C:\Python\Python312\python.exe` / `python3.12` on PATH
|
|
86
|
-
|
|
87
|
-
If none match, run `rrun setup <host>`. It is idempotent and non-destructive: if the machine lacks Python 3.12, a [python-build-standalone](https://github.com/astral-sh/python-build-standalone) tarball is downloaded to the local cache (`~/.cache/rrun/`) and streamed over ssh stdin — no registry, no PATH changes, no admin rights, and the remote never touches GitHub. The venv gets an Alibaba Cloud pip mirror in its own `pip.ini`/`pip.conf` (global config untouched) plus the standard packages from `remote-requirements.txt` (override with `~/.rrun/remote-requirements.txt`).
|
|
88
|
-
|
|
89
|
-
## Auditing & state
|
|
90
|
-
|
|
91
|
-
- Every execution appends a JSON line to `~/.rrun/log/remote-exec.jsonl` (host, lang, script sha1, exit code, duration — never passwords; env vars recorded as keys only).
|
|
92
|
-
- Inline `-c` payloads are archived under `~/.rrun/drops/` for replay.
|
|
93
|
-
- Root directory overridable via `$RRUN_HOME`.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## 中文简介
|
|
98
|
-
|
|
99
|
-
rrun 解决「从 POSIX shell 在远程机器(尤其 Windows)执行脚本」的转码/转义地狱:
|
|
100
|
-
脚本内容一律走 **stdin 管道**(UTF-8,中文随便用),命令行参数只放行 ASCII;
|
|
101
|
-
PowerShell 载荷编码为单行纯 ASCII wrapper,退出码原样透传。
|
|
102
|
-
|
|
103
|
-
- 安装:`pipx install rrun-cli`(控制端需 `ssh` + `sshpass`;装好后命令是 `rrun`)
|
|
104
|
-
- 机器清单 `machines.json` 来源链:`$RRUN_CONFIG` → `./machines.json` → `~/.rrun/machines.json` → `~/.rrun/machines.d/*.json` → 旧位置兼容
|
|
105
|
-
- 远程 python 基线锁 3.12,`rrun setup <host>` 一键幂等初始化统一 venv(standalone 基座经 ssh 推流安装,远端无需访问 GitHub,pip 走阿里云镜像)
|
|
106
|
-
- 更多踩坑约定见 [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md)
|
|
107
|
-
|
|
108
|
-
## License
|
|
109
|
-
|
|
110
|
-
MIT
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: rrun-cli
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Run local scripts on remote machines over SSH via stdin pipes — no escaping/encoding hell. Supports python/powershell/bash, with a unified remote Python 3.12 venv provisioner.
|
|
5
|
-
Author: waqiju
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/waqiju/rrun
|
|
8
|
-
Project-URL: Repository, https://github.com/waqiju/rrun
|
|
9
|
-
Project-URL: Issues, https://github.com/waqiju/rrun/issues
|
|
10
|
-
Keywords: ssh,remote-exec,powershell,windows,ops,automation
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: Intended Audience :: System Administrators
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
-
Classifier: Operating System :: MacOS
|
|
18
|
-
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Topic :: System :: Systems Administration
|
|
20
|
-
Requires-Python: >=3.10
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Dynamic: license-file
|
|
24
|
-
|
|
25
|
-
# rrun
|
|
26
|
-
|
|
27
|
-
Run local scripts on remote machines over SSH — **via stdin pipes, never via command-line arguments** — so quoting, escaping, and CJK/UTF-8 encoding survive the `bash → ssh → cmd/powershell` journey intact.
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
rrun exec my-win-box ./deploy.ps1 # powershell (inferred from .ps1)
|
|
31
|
-
rrun exec my-mac ./build.sh # bash
|
|
32
|
-
rrun exec my-win-box ./report.py # python (remote 3.12, -X utf8)
|
|
33
|
-
rrun exec my-win-box -c "Get-Date" # inline content (archived to ~/.rrun/drops/)
|
|
34
|
-
rrun exec my-mac ./etl.py -- arg1 arg2 # ASCII args pass-through
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Remote stdout → your stdout, remote stderr → your stderr, and the **exit code is passed through** unchanged (255 = ssh transport failure, 124 = local `--timeout`). Pipes, `&&`, and CI integration just work.
|
|
38
|
-
|
|
39
|
-
## Why
|
|
40
|
-
|
|
41
|
-
Running commands on remote Windows machines from a POSIX shell is a minefield: sshd lands you in `cmd` with a GBK codepage, quotes and `$` get eaten by one of three shells along the way, and any non-ASCII argument gets mojibake'd. rrun's rules:
|
|
42
|
-
|
|
43
|
-
- Script content (UTF-8, Chinese welcome) is always piped through **stdin** — never the command line.
|
|
44
|
-
- For PowerShell, the payload is base64-wrapped into a **single-line pure-ASCII wrapper** (`powershell -Command -` executes stdin line-by-line, multi-line fails silently), decoded and invoked as a ScriptBlock remotely.
|
|
45
|
-
- Command-line arguments are restricted to ASCII and passed through safely (`sys.argv` / `$@` / `$args`). Put anything fancier in the script or a JSON file.
|
|
46
|
-
|
|
47
|
-
See [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md) (中文) for the full set of hard-won conventions.
|
|
48
|
-
|
|
49
|
-
## Install
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
pipx install rrun-cli # recommended: isolated global CLI (provides the `rrun` command)
|
|
53
|
-
# or: pip install rrun-cli
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
> The PyPI distribution is named `rrun-cli` (plain `rrun` is on PyPI's prohibited-name list
|
|
57
|
-
> as it's confusable with `run`), but the installed command is `rrun` — plus the legacy alias
|
|
58
|
-
> `remote-machine`.
|
|
59
|
-
|
|
60
|
-
Both `rrun` and the legacy alias `remote-machine` are installed.
|
|
61
|
-
|
|
62
|
-
**Control machine requirements:** `ssh` + `sshpass` (`sudo apt install sshpass`), Python ≥ 3.10. Zero third-party Python dependencies.
|
|
63
|
-
**Remote machines:** OpenSSH server. Windows remotes execute via PowerShell; Mac/Linux via bash.
|
|
64
|
-
|
|
65
|
-
## Configuration: machines.json
|
|
66
|
-
|
|
67
|
-
Credentials live in a local `machines.json` (never committed — it's a local file, `chmod 600` recommended). See [machines.template.json](machines.template.json):
|
|
68
|
-
|
|
69
|
-
```json
|
|
70
|
-
{
|
|
71
|
-
"defaults": { "windows": { "os": "Windows" } },
|
|
72
|
-
"machines": [
|
|
73
|
-
{ "name": "my-win-box", "ip": "192.168.1.10", "os": "Windows",
|
|
74
|
-
"user": "admin", "password": "secret" }
|
|
75
|
-
]
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Sources are merged by machine name, highest priority first (all optional, failures skipped silently):
|
|
80
|
-
|
|
81
|
-
1. `$RRUN_CONFIG` (os.pathsep-separated, multiple files allowed)
|
|
82
|
-
2. `$REMOTE_MACHINE_CONFIG` (legacy name, still honored)
|
|
83
|
-
3. `./machines.json` (current working directory)
|
|
84
|
-
4. `~/.rrun/machines.json`
|
|
85
|
-
5. `~/.rrun/machines.d/*.json` (sorted by filename — point each inventory at its own file/symlink)
|
|
86
|
-
6. Legacy: `~/.remote-machine/machines.json` (POSIX) / `C:\tools\remote-machine\machines.json` (Windows)
|
|
87
|
-
|
|
88
|
-
Per-file `defaults` sections (keyed by `os`, lowercased) are applied before merging. Inspect the chain with `rrun config`; list machines (redacted) with `rrun machines`.
|
|
89
|
-
|
|
90
|
-
## Subcommands
|
|
91
|
-
|
|
92
|
-
| Command | Purpose |
|
|
93
|
-
|---|---|
|
|
94
|
-
| `rrun exec <host> <script\|-c ...>` | Execute a local script / inline content remotely |
|
|
95
|
-
| `rrun machines [--json]` | List merged machines (redacted, with source) |
|
|
96
|
-
| `rrun config` | Diagnose the machines.json source chain |
|
|
97
|
-
| `rrun setup <host\|--all> [--force]` | Provision the unified remote Python 3.12 venv (idempotent) |
|
|
98
|
-
| `rrun pip <host> -- list` | Run pip inside the remote unified venv |
|
|
99
|
-
| `rrun close [<host>\|--all]` | Close ssh ControlMaster multiplexed connections |
|
|
100
|
-
|
|
101
|
-
Useful `exec` flags: `--lang bash|powershell|python`, `--workdir`, `--env K=V`, `--timeout`, `--python <path>` (skip detection), `--no-mux`, `-q`.
|
|
102
|
-
|
|
103
|
-
## Remote Python: unified 3.12 environment
|
|
104
|
-
|
|
105
|
-
For `python` scripts, rrun requires a **3.12.x** interpreter on the remote, probed in order:
|
|
106
|
-
|
|
107
|
-
1. Unified venv: `C:\tools\remote-machine\venv\Scripts\python.exe` / `~/.remote-machine/venv/bin/python`
|
|
108
|
-
2. Standalone base: `...\python312\python.exe` / `~/.remote-machine/python312/bin/python3`
|
|
109
|
-
3. Existing installs: `C:\Python\Python312\python.exe` / `python3.12` on PATH
|
|
110
|
-
|
|
111
|
-
If none match, run `rrun setup <host>`. It is idempotent and non-destructive: if the machine lacks Python 3.12, a [python-build-standalone](https://github.com/astral-sh/python-build-standalone) tarball is downloaded to the local cache (`~/.cache/rrun/`) and streamed over ssh stdin — no registry, no PATH changes, no admin rights, and the remote never touches GitHub. The venv gets an Alibaba Cloud pip mirror in its own `pip.ini`/`pip.conf` (global config untouched) plus the standard packages from `remote-requirements.txt` (override with `~/.rrun/remote-requirements.txt`).
|
|
112
|
-
|
|
113
|
-
## Auditing & state
|
|
114
|
-
|
|
115
|
-
- Every execution appends a JSON line to `~/.rrun/log/remote-exec.jsonl` (host, lang, script sha1, exit code, duration — never passwords; env vars recorded as keys only).
|
|
116
|
-
- Inline `-c` payloads are archived under `~/.rrun/drops/` for replay.
|
|
117
|
-
- Root directory overridable via `$RRUN_HOME`.
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## 中文简介
|
|
122
|
-
|
|
123
|
-
rrun 解决「从 POSIX shell 在远程机器(尤其 Windows)执行脚本」的转码/转义地狱:
|
|
124
|
-
脚本内容一律走 **stdin 管道**(UTF-8,中文随便用),命令行参数只放行 ASCII;
|
|
125
|
-
PowerShell 载荷编码为单行纯 ASCII wrapper,退出码原样透传。
|
|
126
|
-
|
|
127
|
-
- 安装:`pipx install rrun-cli`(控制端需 `ssh` + `sshpass`;装好后命令是 `rrun`)
|
|
128
|
-
- 机器清单 `machines.json` 来源链:`$RRUN_CONFIG` → `./machines.json` → `~/.rrun/machines.json` → `~/.rrun/machines.d/*.json` → 旧位置兼容
|
|
129
|
-
- 远程 python 基线锁 3.12,`rrun setup <host>` 一键幂等初始化统一 venv(standalone 基座经 ssh 推流安装,远端无需访问 GitHub,pip 走阿里云镜像)
|
|
130
|
-
- 更多踩坑约定见 [docs/remote-exec-conventions.md](docs/remote-exec-conventions.md)
|
|
131
|
-
|
|
132
|
-
## License
|
|
133
|
-
|
|
134
|
-
MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|