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.
@@ -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
+ [![PyPI](https://img.shields.io/pypi/v/rrun-cli)](https://pypi.org/project/rrun-cli/)
29
+ [![Python](https://img.shields.io/pypi/pyversions/rrun-cli)](https://pypi.org/project/rrun-cli/)
30
+ [![License](https://img.shields.io/pypi/l/rrun-cli)](https://github.com/waqiju/rrun/blob/main/LICENSE)
31
+ [![publish](https://github.com/waqiju/rrun/actions/workflows/publish.yml/badge.svg)](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)
@@ -0,0 +1,150 @@
1
+ # rrun
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/rrun-cli)](https://pypi.org/project/rrun-cli/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/rrun-cli)](https://pypi.org/project/rrun-cli/)
5
+ [![License](https://img.shields.io/pypi/l/rrun-cli)](https://github.com/waqiju/rrun/blob/main/LICENSE)
6
+ [![publish](https://github.com/waqiju/rrun/actions/workflows/publish.yml/badge.svg)](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>=68"]
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.0"
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 = { text = "MIT" }
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"
@@ -12,7 +12,7 @@ try:
12
12
 
13
13
  __version__ = _pkg_version("rrun-cli")
14
14
  except Exception: # noqa: BLE001 - 未安装(源码直跑)时回退
15
- __version__ = "0.1.0"
15
+ __version__ = "0.1.1"
16
16
 
17
17
  from .executor import (
18
18
  AUDIT_LOG,
@@ -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
+ [![PyPI](https://img.shields.io/pypi/v/rrun-cli)](https://pypi.org/project/rrun-cli/)
29
+ [![Python](https://img.shields.io/pypi/pyversions/rrun-cli)](https://pypi.org/project/rrun-cli/)
30
+ [![License](https://img.shields.io/pypi/l/rrun-cli)](https://github.com/waqiju/rrun/blob/main/LICENSE)
31
+ [![publish](https://github.com/waqiju/rrun/actions/workflows/publish.yml/badge.svg)](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