cloudx-proxy 0.17.2__tar.gz → 0.17.4__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.
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/context/development.md +26 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/context/ssh-config.md +34 -3
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/CHANGELOG.md +22 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/PKG-INFO +30 -8
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/README.md +29 -7
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy/_version.py +2 -2
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy/cli.py +32 -13
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy/setup.py +450 -98
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy.egg-info/PKG-INFO +30 -8
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/tests/test_backward_compat.py +54 -10
- cloudx_proxy-0.17.4/tests/test_cli_options.py +418 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/tests/test_runtime_safety.py +83 -1
- cloudx_proxy-0.17.4/tests/test_ssh_config.py +919 -0
- cloudx_proxy-0.17.2/tests/test_cli_options.py +0 -65
- cloudx_proxy-0.17.2/tests/test_ssh_config.py +0 -470
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/README.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/context/README.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/context/architecture.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/context/commit-conventions.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/context/project-overview.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/roadmap/architecture-review.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/roadmap/documentation-findings.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.ai/roadmap/improvement-roadmap.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.clinerules +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.envrc +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.github/dependabot.yml +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.github/workflows/claude.yml +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.github/workflows/codeql.yml +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.github/workflows/release.yml +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.github/workflows/test.yml +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.gitignore +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/.releaserc +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/CLAUDE.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/CONTRIBUTING.md +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/LICENSE +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/NOTICE +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy/__init__.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy/_op.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy/colors.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy/core.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy.egg-info/SOURCES.txt +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy.egg-info/dependency_links.txt +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy.egg-info/entry_points.txt +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy.egg-info/requires.txt +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/cloudx_proxy.egg-info/top_level.txt +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/pyproject.toml +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/setup.cfg +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/tests/__init__.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/tests/test_config_safety.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/tests/test_core.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/tests/test_non_interactive.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/tests/test_setup.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/tests/test_version.py +0 -0
- {cloudx_proxy-0.17.2 → cloudx_proxy-0.17.4}/uv.lock +0 -0
|
@@ -104,6 +104,32 @@ The project uses semantic-release with GitHub Actions:
|
|
|
104
104
|
- `feat:` → minor version
|
|
105
105
|
- `fix:`, `docs:`, `style:`, etc. → patch version
|
|
106
106
|
|
|
107
|
+
## Console Output
|
|
108
|
+
|
|
109
|
+
Three indents and three symbols, used the same way everywhere, so a run can be
|
|
110
|
+
skimmed down the left edge. The rule lives next to `CloudXSetup.print_status`
|
|
111
|
+
in `cloudx_proxy/setup.py`; `tests/test_cli_options.py` enforces it.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
=== Section === a phase of the command (print_header)
|
|
115
|
+
○ Doing something... STEP: one operation within the section
|
|
116
|
+
✓ It worked DETAIL: what that step found or did
|
|
117
|
+
○ ... SUB: detail of a nested operation
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- `○` neutral - about to happen, in progress, informational, or a dry-run
|
|
121
|
+
preview
|
|
122
|
+
- `✓` true now - succeeded, exists, verified
|
|
123
|
+
- `✗` wrong - failed, missing, invalid
|
|
124
|
+
|
|
125
|
+
Indents are 0, 2 and 4; nothing else. A detail must have a step above it, and a
|
|
126
|
+
sub-detail a detail. A ✗ marks the *outcome*: a condition the code goes on to
|
|
127
|
+
recover from is `○`, so one failure shows as one ✗ rather than one per line on
|
|
128
|
+
the way there.
|
|
129
|
+
|
|
130
|
+
`print_header` emits a single blank line before the header, and callers printing
|
|
131
|
+
a banner above the first section add none of their own - the two used to stack.
|
|
132
|
+
|
|
107
133
|
## Publishing to PyPI
|
|
108
134
|
|
|
109
135
|
The package is automatically published to PyPI via GitHub Actions when a new release is created. Setup:
|
|
@@ -66,7 +66,14 @@ Host cloudX-Prod-*
|
|
|
66
66
|
|
|
67
67
|
**Settings explained**:
|
|
68
68
|
- `IdentityFile`: Path to the SSH private key for this environment
|
|
69
|
-
- `IdentitiesOnly yes`: Only use the specified key, don't try others from ssh-agent
|
|
69
|
+
- `IdentitiesOnly yes`: Only use the specified key, don't try others from ssh-agent.
|
|
70
|
+
With 1Password this is why `IdentityFile` names the *public* key: ssh offers only
|
|
71
|
+
identities named by `IdentityFile`, even when the agent holds more, so the `.pub`
|
|
72
|
+
is what lets the agent's copy of the key be used at all.
|
|
73
|
+
- `IdentityAgent`: Only written with `--1password`, and only where the agent has to be
|
|
74
|
+
named: `~/.1password/agent.sock` on macOS and Linux (or the snap path on snap
|
|
75
|
+
installs). Nothing is written on Windows, where 1Password serves the standard
|
|
76
|
+
OpenSSH named pipe `\\.\pipe\openssh-ssh-agent` that ssh already uses by default.
|
|
70
77
|
- `ProxyCommand`: The cloudX-proxy connect command that:
|
|
71
78
|
- Checks if the instance is running (starts it if needed)
|
|
72
79
|
- Pushes the SSH public key to the instance
|
|
@@ -101,13 +108,37 @@ Host cloudX-Prod-foobar
|
|
|
101
108
|
- The `Environment` tag determines the environment part
|
|
102
109
|
- The `Name` tag (or user-specified hostname) determines the hostname part
|
|
103
110
|
|
|
111
|
+
## Prefix Case
|
|
112
|
+
|
|
113
|
+
ssh matches `Host` **patterns** case-sensitively, and the pattern syntax supports
|
|
114
|
+
only `*` and `?` - `cloud[xX]-*` is a literal hostname, not a character class. A
|
|
115
|
+
block written as `Host cloudX-*` therefore does not apply to a host entry spelled
|
|
116
|
+
`cloudx-dev-web1`, and both spellings exist in the wild (two command names, older
|
|
117
|
+
releases, hand edits).
|
|
118
|
+
|
|
119
|
+
Wildcard blocks are consequently written with one pattern per prefix spelling -
|
|
120
|
+
`Host cloudX-* cloudx-*` - since a `Host` line takes any number of patterns and
|
|
121
|
+
matches if any one of them does. The configured spelling always comes first.
|
|
122
|
+
|
|
123
|
+
Only the prefix varies between the patterns. The environment part is set by
|
|
124
|
+
whoever rolled out the environment stack and the host part is chosen by the user;
|
|
125
|
+
both are written exactly as given.
|
|
126
|
+
|
|
127
|
+
Host entries are never rewritten. `cloudX` is the product's name - the X is ten,
|
|
128
|
+
after Cloud9 - but people who would rather not reach for shift call their instance
|
|
129
|
+
`cloudx-dev-something`, and that name is theirs: it is what they type, what `list`
|
|
130
|
+
reports and what VSCode offers. The widened wildcard blocks match it either way, so
|
|
131
|
+
it does not need renaming to inherit its settings. Re-running `setup` for an
|
|
132
|
+
existing host updates its `HostName` and keeps its name; only a brand new entry is
|
|
133
|
+
written in the configured case.
|
|
134
|
+
|
|
104
135
|
## Configuration Inheritance
|
|
105
136
|
|
|
106
137
|
SSH applies configurations from most specific to least specific. When connecting to `cloudX-Prod-foobar`:
|
|
107
138
|
|
|
108
139
|
1. **Host cloudX-Prod-foobar** matches first → sets `HostName`
|
|
109
|
-
2. **Host cloudX-Prod-*** matches next → sets `IdentityFile`, `IdentitiesOnly`, `ProxyCommand`
|
|
110
|
-
3. **Host cloudX-*** matches last → sets `User`, `TCPKeepAlive`, `Control*`
|
|
140
|
+
2. **Host cloudX-Prod-* cloudx-Prod-*** matches next → sets `IdentityFile`, `IdentitiesOnly`, `ProxyCommand`
|
|
141
|
+
3. **Host cloudX-* cloudx-*** matches last → sets `User`, `TCPKeepAlive`, `Control*`
|
|
111
142
|
|
|
112
143
|
The result is a fully configured connection with all necessary settings.
|
|
113
144
|
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## [0.17.4](https://github.com/easytocloud/cloudX-proxy/compare/v0.17.3...v0.17.4) (2026-09-06)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* cleanup no longer deletes ProxyCommand flags ([5958bba](https://github.com/easytocloud/cloudX-proxy/commit/5958bbaa4db313d0ec4cd5aa045f5a39f08a0ec3))
|
|
7
|
+
* detect the command name on Windows too ([3f537c4](https://github.com/easytocloud/cloudX-proxy/commit/3f537c44c080e5eb8b7d66f02458bd2874bc379e))
|
|
8
|
+
* dry run previews the patterns it will actually write ([d0911ac](https://github.com/easytocloud/cloudX-proxy/commit/d0911acdd24e80d3097654842516638ecf93382f))
|
|
9
|
+
* find the 1Password SSH agent per platform ([8ae4860](https://github.com/easytocloud/cloudX-proxy/commit/8ae48605643daded0f40850a5e53446940da2fe4))
|
|
10
|
+
* list shows patterns in the preferred spelling ([0c49ec5](https://github.com/easytocloud/cloudX-proxy/commit/0c49ec5202c4dda1c6e43a42c5cc15e5d10efb09))
|
|
11
|
+
* never rename a host entry to the command name's case ([1b19d32](https://github.com/easytocloud/cloudX-proxy/commit/1b19d329b73484dc0601b7393968184c22dab62e))
|
|
12
|
+
* stop stacking blank lines, and name the host that was written ([3e24b87](https://github.com/easytocloud/cloudX-proxy/commit/3e24b87e55fa67dc7587909180af210f12f25bc3))
|
|
13
|
+
* wildcard Host blocks match every spelling of the prefix ([4532ecf](https://github.com/easytocloud/cloudX-proxy/commit/4532ecf97d31c4cfa06181a1c19675b53bf56c31))
|
|
14
|
+
* write managed Host lines in one prefix case ([b551fe1](https://github.com/easytocloud/cloudX-proxy/commit/b551fe17325f4cbfb742eff980c833416d3208bc))
|
|
15
|
+
|
|
16
|
+
## [0.17.3](https://github.com/easytocloud/cloudX-proxy/compare/v0.17.2...v0.17.3) (2026-09-04)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* connectivity check reports false failure on healthy connections ([29e3717](https://github.com/easytocloud/cloudX-proxy/commit/29e3717d4d3d85f0f37290d597d79a3e99b6e637))
|
|
22
|
+
|
|
1
23
|
## [0.17.2](https://github.com/easytocloud/cloudX-proxy/compare/v0.17.1...v0.17.2) (2026-09-04)
|
|
2
24
|
|
|
3
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudx-proxy
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.4
|
|
4
4
|
Summary: SSH proxy command to connect VSCode with cloudX instance using AWS Systems Manager
|
|
5
5
|
Author-email: easytocloud <info@easytocloud.com>
|
|
6
6
|
License: MIT License
|
|
@@ -304,7 +304,7 @@ Will create a three-tier configuration structure like this:
|
|
|
304
304
|
# Generic configuration (shared by all environments)
|
|
305
305
|
# Created by cloudX-proxy v1.0.0 on 2025-03-07 09:05:23
|
|
306
306
|
# Configuration type: generic
|
|
307
|
-
Host cloudX-*
|
|
307
|
+
Host cloudX-* cloudx-*
|
|
308
308
|
User ec2-user
|
|
309
309
|
TCPKeepAlive yes
|
|
310
310
|
ControlMaster auto
|
|
@@ -314,7 +314,7 @@ Host cloudX-*
|
|
|
314
314
|
# Environment configuration (specific to a single environment)
|
|
315
315
|
# Created by cloudX-proxy v1.0.0 on 2025-03-07 09:05:23
|
|
316
316
|
# Configuration type: environment
|
|
317
|
-
Host cloudX-dev-*
|
|
317
|
+
Host cloudX-dev-* cloudx-dev-*
|
|
318
318
|
IdentityFile ~/.ssh/cloudX/mykey
|
|
319
319
|
IdentitiesOnly yes
|
|
320
320
|
ProxyCommand uvx cloudX-proxy connect %h %p --profile myprofile --ssh-key mykey --ssh-dir ~/.ssh/cloudX
|
|
@@ -481,8 +481,23 @@ Understanding the connection flow helps with troubleshooting and explains why ce
|
|
|
481
481
|
### Command Line Options
|
|
482
482
|
|
|
483
483
|
> **Note:** Both `cloudX-proxy` (preferred) and `cloudx-proxy` command names are available. The command name determines the prefix case used in SSH configurations:
|
|
484
|
-
> - `cloudX-proxy` generates `Host cloudX-*` patterns (preferred)
|
|
485
|
-
> - `cloudx-proxy` generates `Host cloudx-*` patterns
|
|
484
|
+
> - `cloudX-proxy` generates `Host cloudX-* cloudx-*` patterns (preferred)
|
|
485
|
+
> - `cloudx-proxy` generates `Host cloudx-* cloudX-*` patterns
|
|
486
|
+
>
|
|
487
|
+
> ssh matches `Host` patterns case-sensitively, and its pattern syntax has only
|
|
488
|
+
> `*` and `?` - there is no `[xX]` character class. Wildcard blocks therefore list
|
|
489
|
+
> both spellings, so a config that mixes them keeps working; the first pattern is
|
|
490
|
+
> the one the command name chose.
|
|
491
|
+
>
|
|
492
|
+
> Only the prefix varies. The environment part is fixed by whoever rolled out the
|
|
493
|
+
> environment stack, and the host part is chosen by the user - both are written
|
|
494
|
+
> exactly as given. An instance called `cloudx-dev-web1` keeps that name and still
|
|
495
|
+
> picks up its settings from `Host cloudX-dev-* cloudx-dev-*`.
|
|
496
|
+
>
|
|
497
|
+
> `list` reflects that split: hosts appear under the names their owners gave them,
|
|
498
|
+
> while patterns are shown as `cloudX-*` whichever command name you type, since
|
|
499
|
+
> `cloudX` is the preferred spelling. An environment with no hosts of its own is
|
|
500
|
+
> listed only as a pattern (under `--detailed`), not as an environment.
|
|
486
501
|
>
|
|
487
502
|
> Run `cleanup` with your preferred command to normalize existing configurations.
|
|
488
503
|
|
|
@@ -620,8 +635,13 @@ uvx cloudX-proxy cleanup --ssh-config ~/.ssh/custom/config
|
|
|
620
635
|
```
|
|
621
636
|
|
|
622
637
|
**Prefix Normalization:** The cleanup command normalizes all host patterns and ProxyCommand references to match the command name used:
|
|
623
|
-
- Running `cloudX-proxy cleanup` converts `Host cloudx-*` → `Host cloudX-*` and `uvx cloudx-proxy` → `uvx cloudX-proxy`
|
|
624
|
-
- Running `cloudx-proxy cleanup` converts `Host cloudX-*` → `Host cloudx-*` and `uvx cloudX-proxy` → `uvx cloudx-proxy`
|
|
638
|
+
- Running `cloudX-proxy cleanup` converts `Host cloudx-*` → `Host cloudX-* cloudx-*` and `uvx cloudx-proxy` → `uvx cloudX-proxy`
|
|
639
|
+
- Running `cloudx-proxy cleanup` converts `Host cloudX-*` → `Host cloudx-* cloudX-*` and `uvx cloudX-proxy` → `uvx cloudx-proxy`
|
|
640
|
+
|
|
641
|
+
Wildcard blocks keep both spellings so that host entries in either case pick up
|
|
642
|
+
`User`, `IdentitiesOnly` and the `ProxyCommand`. Host entries are **not** renamed:
|
|
643
|
+
what an instance is called belongs to whoever created it. Re-running `setup` for a
|
|
644
|
+
host that already exists updates its instance id and leaves its name alone.
|
|
625
645
|
|
|
626
646
|
This allows users to easily convert between naming conventions. The preferred convention is `cloudX` (uppercase X).
|
|
627
647
|
|
|
@@ -727,7 +747,9 @@ These permissions are required to bootstrap the instance, so that after creation
|
|
|
727
747
|
- **Region mismatch** - Ensure AWS profile region matches instance location
|
|
728
748
|
|
|
729
749
|
4. **SSH Key Issues**
|
|
730
|
-
- If using 1Password SSH agent, verify agent is running
|
|
750
|
+
- If using 1Password SSH agent, verify the agent is running. Where it lives depends on the platform:
|
|
751
|
+
* macOS/Linux: `~/.1password/agent.sock` exists (Linux snap installs: `~/snap/1password/current/.1password/agent.sock`)
|
|
752
|
+
* Windows: 1Password serves the standard OpenSSH named pipe `\\.\pipe\openssh-ssh-agent`, which ssh uses by default - there is no socket file and no `IdentityAgent` line is written
|
|
731
753
|
- Check file permissions (600 for private key, 644 for public key)
|
|
732
754
|
- Verify the public key is being successfully pushed to the instance
|
|
733
755
|
- For 1Password-managed keys, make sure:
|
|
@@ -252,7 +252,7 @@ Will create a three-tier configuration structure like this:
|
|
|
252
252
|
# Generic configuration (shared by all environments)
|
|
253
253
|
# Created by cloudX-proxy v1.0.0 on 2025-03-07 09:05:23
|
|
254
254
|
# Configuration type: generic
|
|
255
|
-
Host cloudX-*
|
|
255
|
+
Host cloudX-* cloudx-*
|
|
256
256
|
User ec2-user
|
|
257
257
|
TCPKeepAlive yes
|
|
258
258
|
ControlMaster auto
|
|
@@ -262,7 +262,7 @@ Host cloudX-*
|
|
|
262
262
|
# Environment configuration (specific to a single environment)
|
|
263
263
|
# Created by cloudX-proxy v1.0.0 on 2025-03-07 09:05:23
|
|
264
264
|
# Configuration type: environment
|
|
265
|
-
Host cloudX-dev-*
|
|
265
|
+
Host cloudX-dev-* cloudx-dev-*
|
|
266
266
|
IdentityFile ~/.ssh/cloudX/mykey
|
|
267
267
|
IdentitiesOnly yes
|
|
268
268
|
ProxyCommand uvx cloudX-proxy connect %h %p --profile myprofile --ssh-key mykey --ssh-dir ~/.ssh/cloudX
|
|
@@ -429,8 +429,23 @@ Understanding the connection flow helps with troubleshooting and explains why ce
|
|
|
429
429
|
### Command Line Options
|
|
430
430
|
|
|
431
431
|
> **Note:** Both `cloudX-proxy` (preferred) and `cloudx-proxy` command names are available. The command name determines the prefix case used in SSH configurations:
|
|
432
|
-
> - `cloudX-proxy` generates `Host cloudX-*` patterns (preferred)
|
|
433
|
-
> - `cloudx-proxy` generates `Host cloudx-*` patterns
|
|
432
|
+
> - `cloudX-proxy` generates `Host cloudX-* cloudx-*` patterns (preferred)
|
|
433
|
+
> - `cloudx-proxy` generates `Host cloudx-* cloudX-*` patterns
|
|
434
|
+
>
|
|
435
|
+
> ssh matches `Host` patterns case-sensitively, and its pattern syntax has only
|
|
436
|
+
> `*` and `?` - there is no `[xX]` character class. Wildcard blocks therefore list
|
|
437
|
+
> both spellings, so a config that mixes them keeps working; the first pattern is
|
|
438
|
+
> the one the command name chose.
|
|
439
|
+
>
|
|
440
|
+
> Only the prefix varies. The environment part is fixed by whoever rolled out the
|
|
441
|
+
> environment stack, and the host part is chosen by the user - both are written
|
|
442
|
+
> exactly as given. An instance called `cloudx-dev-web1` keeps that name and still
|
|
443
|
+
> picks up its settings from `Host cloudX-dev-* cloudx-dev-*`.
|
|
444
|
+
>
|
|
445
|
+
> `list` reflects that split: hosts appear under the names their owners gave them,
|
|
446
|
+
> while patterns are shown as `cloudX-*` whichever command name you type, since
|
|
447
|
+
> `cloudX` is the preferred spelling. An environment with no hosts of its own is
|
|
448
|
+
> listed only as a pattern (under `--detailed`), not as an environment.
|
|
434
449
|
>
|
|
435
450
|
> Run `cleanup` with your preferred command to normalize existing configurations.
|
|
436
451
|
|
|
@@ -568,8 +583,13 @@ uvx cloudX-proxy cleanup --ssh-config ~/.ssh/custom/config
|
|
|
568
583
|
```
|
|
569
584
|
|
|
570
585
|
**Prefix Normalization:** The cleanup command normalizes all host patterns and ProxyCommand references to match the command name used:
|
|
571
|
-
- Running `cloudX-proxy cleanup` converts `Host cloudx-*` → `Host cloudX-*` and `uvx cloudx-proxy` → `uvx cloudX-proxy`
|
|
572
|
-
- Running `cloudx-proxy cleanup` converts `Host cloudX-*` → `Host cloudx-*` and `uvx cloudX-proxy` → `uvx cloudx-proxy`
|
|
586
|
+
- Running `cloudX-proxy cleanup` converts `Host cloudx-*` → `Host cloudX-* cloudx-*` and `uvx cloudx-proxy` → `uvx cloudX-proxy`
|
|
587
|
+
- Running `cloudx-proxy cleanup` converts `Host cloudX-*` → `Host cloudx-* cloudX-*` and `uvx cloudX-proxy` → `uvx cloudx-proxy`
|
|
588
|
+
|
|
589
|
+
Wildcard blocks keep both spellings so that host entries in either case pick up
|
|
590
|
+
`User`, `IdentitiesOnly` and the `ProxyCommand`. Host entries are **not** renamed:
|
|
591
|
+
what an instance is called belongs to whoever created it. Re-running `setup` for a
|
|
592
|
+
host that already exists updates its instance id and leaves its name alone.
|
|
573
593
|
|
|
574
594
|
This allows users to easily convert between naming conventions. The preferred convention is `cloudX` (uppercase X).
|
|
575
595
|
|
|
@@ -675,7 +695,9 @@ These permissions are required to bootstrap the instance, so that after creation
|
|
|
675
695
|
- **Region mismatch** - Ensure AWS profile region matches instance location
|
|
676
696
|
|
|
677
697
|
4. **SSH Key Issues**
|
|
678
|
-
- If using 1Password SSH agent, verify agent is running
|
|
698
|
+
- If using 1Password SSH agent, verify the agent is running. Where it lives depends on the platform:
|
|
699
|
+
* macOS/Linux: `~/.1password/agent.sock` exists (Linux snap installs: `~/snap/1password/current/.1password/agent.sock`)
|
|
700
|
+
* Windows: 1Password serves the standard OpenSSH named pipe `\\.\pipe\openssh-ssh-agent`, which ssh uses by default - there is no socket file and no `IdentityAgent` line is written
|
|
679
701
|
- Check file permissions (600 for private key, 644 for public key)
|
|
680
702
|
- Verify the public key is being successfully pushed to the instance
|
|
681
703
|
- For 1Password-managed keys, make sure:
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.17.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 17,
|
|
21
|
+
__version__ = version = '0.17.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 17, 4)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -31,6 +31,20 @@ def detect_ssh_defaults() -> tuple:
|
|
|
31
31
|
return "cloudX", "cloudX", "~/.ssh/cloudX"
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def prefix_from_command_name() -> str:
|
|
35
|
+
"""The host prefix implied by the command name that was invoked.
|
|
36
|
+
|
|
37
|
+
On Windows a console script is an .exe, so sys.argv[0] ends in one and a
|
|
38
|
+
bare basename never equals 'cloudX-proxy' - every Windows user silently
|
|
39
|
+
got the lowercase prefix whichever command they typed. Compare the stem.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
str: 'cloudX' when invoked as cloudX-proxy, otherwise 'cloudx'
|
|
43
|
+
"""
|
|
44
|
+
stem = os.path.splitext(os.path.basename(sys.argv[0]))[0]
|
|
45
|
+
return 'cloudX' if stem == 'cloudX-proxy' else 'cloudx'
|
|
46
|
+
|
|
47
|
+
|
|
34
48
|
def short_host_name(host: str, host_prefix: str) -> str:
|
|
35
49
|
"""Strip '<prefix>-<env>-' off a host entry to get its short name.
|
|
36
50
|
|
|
@@ -188,8 +202,7 @@ def setup(profile: str, ssh_key: str, ssh_config: str, ssh_dir: str, aws_env: st
|
|
|
188
202
|
try:
|
|
189
203
|
# Determine default prefix based on command name if not provided
|
|
190
204
|
if not ssh_host_prefix:
|
|
191
|
-
|
|
192
|
-
ssh_host_prefix = 'cloudX' if cmd_name == 'cloudX-proxy' else 'cloudx'
|
|
205
|
+
ssh_host_prefix = prefix_from_command_name()
|
|
193
206
|
|
|
194
207
|
setup = CloudXSetup(
|
|
195
208
|
profile=profile,
|
|
@@ -205,9 +218,9 @@ def setup(profile: str, ssh_key: str, ssh_config: str, ssh_dir: str, aws_env: st
|
|
|
205
218
|
)
|
|
206
219
|
|
|
207
220
|
if dry_run:
|
|
208
|
-
print(f"\n{header(
|
|
221
|
+
print(f"\n{header('=== cloudX-proxy Setup (DRY RUN) ===')}")
|
|
209
222
|
else:
|
|
210
|
-
print(f"\n{header(
|
|
223
|
+
print(f"\n{header('=== cloudX-proxy Setup ===')}")
|
|
211
224
|
|
|
212
225
|
# Report missing tooling up front rather than from inside a
|
|
213
226
|
# ProxyCommand, where the error is easy to miss
|
|
@@ -340,7 +353,7 @@ def list(ssh_config: str, environment: str, detailed: bool, dry_run: bool):
|
|
|
340
353
|
config_file = cloudx_config
|
|
341
354
|
|
|
342
355
|
if dry_run:
|
|
343
|
-
print(f"\n{header('===
|
|
356
|
+
print(f"\n{header('=== cloudX-proxy List (DRY RUN) ===')}\n")
|
|
344
357
|
print(f"[DRY RUN] Would read SSH config from: {config_file}")
|
|
345
358
|
if environment:
|
|
346
359
|
print(f"[DRY RUN] Would filter hosts by environment: {environment}")
|
|
@@ -355,8 +368,7 @@ def list(ssh_config: str, environment: str, detailed: bool, dry_run: bool):
|
|
|
355
368
|
sys.exit(1)
|
|
356
369
|
|
|
357
370
|
# Detect ssh_host_prefix from command name
|
|
358
|
-
|
|
359
|
-
ssh_host_prefix = 'cloudX' if cmd_name == 'cloudX-proxy' else 'cloudx'
|
|
371
|
+
ssh_host_prefix = prefix_from_command_name()
|
|
360
372
|
|
|
361
373
|
# Use shared parser from CloudXSetup
|
|
362
374
|
setup = CloudXSetup(ssh_config=str(config_file), ssh_host_prefix=ssh_host_prefix)
|
|
@@ -367,17 +379,25 @@ def list(ssh_config: str, environment: str, detailed: bool, dry_run: bool):
|
|
|
367
379
|
environments = {}
|
|
368
380
|
generic_hosts = []
|
|
369
381
|
|
|
382
|
+
# Patterns are shown in the preferred spelling whichever command name
|
|
383
|
+
# was typed. A configured host keeps whatever case its owner gave it
|
|
384
|
+
# and is listed under that name; a pattern has no owner, so there is
|
|
385
|
+
# no reason to show 'cloudx-dev-*' to someone who ran cloudX-proxy.
|
|
386
|
+
display_prefix = setup.preferred_host_prefix
|
|
387
|
+
|
|
370
388
|
# Add global pattern if present
|
|
371
389
|
if parsed['global']:
|
|
372
|
-
generic_hosts.append((f"{
|
|
390
|
+
generic_hosts.append((f"{display_prefix}-*", "N/A"))
|
|
373
391
|
|
|
374
392
|
# Process each environment
|
|
375
393
|
for env_key, env_data in parsed['environments'].items():
|
|
376
394
|
# Use original case name for display, fallback to key if not present
|
|
377
395
|
display_name = env_data.get('name', env_key)
|
|
378
396
|
|
|
379
|
-
# Add environment pattern to generic hosts
|
|
380
|
-
|
|
397
|
+
# Add environment pattern to generic hosts. Environments with no
|
|
398
|
+
# hosts of their own appear here and nowhere else, since the
|
|
399
|
+
# listing below is built from host entries.
|
|
400
|
+
generic_hosts.append((f"{display_prefix}-{display_name}-*", "N/A"))
|
|
381
401
|
|
|
382
402
|
# Filter by environment if specified
|
|
383
403
|
if environment and env_key.lower() != environment.lower():
|
|
@@ -426,7 +446,7 @@ def list(ssh_config: str, environment: str, detailed: bool, dry_run: bool):
|
|
|
426
446
|
return
|
|
427
447
|
|
|
428
448
|
# Print header
|
|
429
|
-
print(f"\n{header('===
|
|
449
|
+
print(f"\n{header('=== cloudX-proxy Configured Hosts ===')}\n")
|
|
430
450
|
|
|
431
451
|
# Print generic patterns if any and detailed mode
|
|
432
452
|
if generic_hosts and detailed:
|
|
@@ -514,8 +534,7 @@ def cleanup(ssh_config: str, ssh_host_prefix: str, dry_run: bool):
|
|
|
514
534
|
|
|
515
535
|
# Determine default prefix based on command name if not provided
|
|
516
536
|
if not ssh_host_prefix:
|
|
517
|
-
|
|
518
|
-
ssh_host_prefix = 'cloudX' if cmd_name == 'cloudX-proxy' else 'cloudx'
|
|
537
|
+
ssh_host_prefix = prefix_from_command_name()
|
|
519
538
|
|
|
520
539
|
setup = CloudXSetup(ssh_config=ssh_config, ssh_host_prefix=ssh_host_prefix, dry_run=dry_run)
|
|
521
540
|
|