Skip to content

Commit 135304c

Browse files
committed
Add GitHub Actions CI/CD workflows and code cleanup
- Added comprehensive CI workflow with testing, linting, and type checking - Added release workflow for automated PyPI publishing - Fixed all ruff linting issues (removed unused imports) - Applied black code formatting consistently - Added pytest-cov for coverage reporting - Workflows support Python 3.12 and 3.13 - Release workflow publishes to both TestPyPI (pre-releases) and PyPI (stable)
1 parent c242bb1 commit 135304c

18 files changed

Lines changed: 414 additions & 172 deletions

.github/workflows/ci.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.12", "3.13"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v3
21+
with:
22+
version: "latest"
23+
24+
- name: Set up Python ${{ matrix.python-version }}
25+
run: uv python install ${{ matrix.python-version }}
26+
27+
- name: Install dependencies
28+
run: uv sync --all-extras --dev
29+
30+
- name: Run linting
31+
run: |
32+
uv run ruff check .
33+
uv run black --check .
34+
35+
- name: Run type checking
36+
run: uv run mypy src/
37+
38+
- name: Run tests
39+
run: uv run pytest --cov=pyssm_client --cov-report=xml
40+
41+
- name: Upload coverage to Codecov
42+
uses: codecov/codecov-action@v3
43+
with:
44+
file: ./coverage.xml
45+
fail_ci_if_error: true
46+
47+
build:
48+
runs-on: ubuntu-latest
49+
needs: test
50+
51+
steps:
52+
- uses: actions/checkout@v4
53+
54+
- name: Install uv
55+
uses: astral-sh/setup-uv@v3
56+
with:
57+
version: "latest"
58+
59+
- name: Build package
60+
run: uv build
61+
62+
- name: Upload build artifacts
63+
uses: actions/upload-artifact@v4
64+
with:
65+
name: dist
66+
path: dist/

.github/workflows/release.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*.*.*'
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Install uv
15+
uses: astral-sh/setup-uv@v3
16+
with:
17+
version: "latest"
18+
19+
- name: Build package
20+
run: uv build
21+
22+
- name: Upload build artifacts
23+
uses: actions/upload-artifact@v4
24+
with:
25+
name: dist
26+
path: dist/
27+
28+
test-pypi:
29+
runs-on: ubuntu-latest
30+
needs: build
31+
environment: test-pypi
32+
33+
steps:
34+
- name: Download build artifacts
35+
uses: actions/download-artifact@v4
36+
with:
37+
name: dist
38+
path: dist/
39+
40+
- name: Install uv
41+
uses: astral-sh/setup-uv@v3
42+
with:
43+
version: "latest"
44+
45+
- name: Publish to TestPyPI
46+
run: uv publish --publish-url https://test.pypi.org/legacy/ --token ${{ secrets.TEST_PYPI_API_TOKEN }}
47+
48+
pypi:
49+
runs-on: ubuntu-latest
50+
needs: [build, test-pypi]
51+
environment: pypi
52+
53+
steps:
54+
- name: Download build artifacts
55+
uses: actions/download-artifact@v4
56+
with:
57+
name: dist
58+
path: dist/
59+
60+
- name: Install uv
61+
uses: astral-sh/setup-uv@v3
62+
with:
63+
version: "latest"
64+
65+
- name: Publish to PyPI
66+
run: uv publish --token ${{ secrets.PYPI_API_TOKEN }}
67+
68+
github-release:
69+
runs-on: ubuntu-latest
70+
needs: pypi
71+
permissions:
72+
contents: write
73+
74+
steps:
75+
- uses: actions/checkout@v4
76+
77+
- name: Download build artifacts
78+
uses: actions/download-artifact@v4
79+
with:
80+
name: dist
81+
path: dist/
82+
83+
- name: Create GitHub Release
84+
uses: softprops/action-gh-release@v1
85+
with:
86+
files: dist/*
87+
generate_release_notes: true
88+
env:
89+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Python Session Manager Plugin
1+
# PySSM Client
22

3-
A Python implementation of the AWS Session Manager Plugin. It speaks the same binary protocol as the official Go plugin and supports interactive shell sessions over SSM.
3+
Enhanced Python AWS SSM Session Manager client with interactive sessions, exec, and file transfer support. It speaks the same binary protocol as the official Go plugin and provides additional functionality like library imports and extended CLI commands.
44

55
Highlights:
66
- Interactive SSH-like sessions via SSM (`ssh` subcommand)
@@ -9,21 +9,36 @@ Highlights:
99
- Minimal logging by default; verbose traces with `-v`
1010

1111

12+
## Installation
13+
14+
Install from PyPI:
15+
16+
```bash
17+
pip install pyssm-client
18+
```
19+
20+
Or with uv:
21+
22+
```bash
23+
uv add pyssm-client
24+
```
25+
1226
## Requirements
1327

1428
- Python 3.12+
1529
- AWS credentials discoverable via environment or `~/.aws/credentials`
1630

17-
Install (editable):
31+
## Quick Start
1832

19-
```
20-
uv pip install -e .
21-
```
33+
```bash
34+
# Interactive SSH-like session
35+
pyssm ssh --target i-0123456789abcdef0
2236

23-
Run with `uv`:
37+
# Execute single command
38+
pyssm exec --target i-0123456789abcdef0 --command "ls -la"
2439

25-
```
26-
uv run python -m session_manager_plugin.cli.main --help
40+
# Copy files
41+
pyssm copy ./local-file.txt i-0123456789abcdef0:/tmp/remote-file.txt
2742
```
2843

2944

@@ -35,8 +50,8 @@ The CLI provides four subcommands: `connect`, `ssh`, `exec`, and `copy`.
3550

3651
Starts a new SSM session using `boto3` and connects interactively.
3752

38-
```
39-
uv run python -m session_manager_plugin.cli.main ssh \
53+
```bash
54+
pyssm ssh \
4055
--target i-0123456789abcdef0 \
4156
--region us-west-2 \
4257
--profile myprofile
@@ -68,8 +83,8 @@ Behavior:
6883

6984
Execute a single command on a target instance and return the results with proper exit codes.
7085

71-
```
72-
uv run python -m session_manager_plugin.cli.main exec \
86+
```bash
87+
pyssm exec \
7388
--target i-0123456789abcdef0 \
7489
--command "ls -la /tmp" \
7590
--region us-west-2 \
@@ -93,15 +108,15 @@ This command:
93108

94109
Transfer files to/from targets using base64 encoding over SSM sessions.
95110

96-
```
111+
```bash
97112
# Upload local file to remote
98-
uv run python -m session_manager_plugin.cli.main copy \
113+
pyssm copy \
99114
./local-file.txt i-0123456789abcdef0:/tmp/remote-file.txt \
100115
--region us-west-2 \
101116
--profile myprofile
102117

103118
# Download remote file to local
104-
uv run python -m session_manager_plugin.cli.main copy \
119+
pyssm copy \
105120
i-0123456789abcdef0:/tmp/remote-file.txt ./local-file.txt \
106121
--region us-west-2 \
107122
--profile myprofile
@@ -126,8 +141,8 @@ Connects using session parameters you already have (typical when called by AWS C
126141

127142
JSON form (mimics the AWS CLI invocation):
128143

129-
```
130-
uv run python -m session_manager_plugin.cli.main connect '{
144+
```bash
145+
pyssm connect '{
131146
"SessionId": "dacort-abc123",
132147
"StreamUrl": "wss://ssmmessages.us-west-2.amazonaws.com/v1/data-channel/dacort-abc123?...",
133148
"TokenValue": "...",
@@ -138,8 +153,8 @@ uv run python -m session_manager_plugin.cli.main connect '{
138153

139154
Flag form:
140155

141-
```
142-
uv run python -m session_manager_plugin.cli.main connect \
156+
```bash
157+
pyssm connect \
143158
--session-id dacort-abc123 \
144159
--stream-url wss://... \
145160
--token-value ... \
@@ -161,8 +176,8 @@ You can embed the plugin in your own Python program. There are four convenient l
161176
For programmatic file transfers with progress tracking and verification:
162177

163178
```python
164-
from session_manager_plugin.file_transfer import FileTransferClient
165-
from session_manager_plugin.file_transfer.types import FileTransferOptions
179+
from pyssm_client.file_transfer import FileTransferClient
180+
from pyssm_client.file_transfer.types import FileTransferOptions
166181

167182
client = FileTransferClient()
168183

@@ -193,7 +208,7 @@ success = await client.download_file(
193208
For simple command execution with clean stdout/stderr separation:
194209

195210
```python
196-
from session_manager_plugin.exec import run_command, run_command_sync
211+
from pyssm_client.exec import run_command, run_command_sync
197212

198213
# Async version
199214
result = await run_command(
@@ -219,9 +234,9 @@ if result.exit_code == 0:
219234

220235
```
221236
import asyncio
222-
from session_manager_plugin.cli.types import ConnectArguments
223-
from session_manager_plugin.cli.main import SessionManagerPlugin
224-
from session_manager_plugin.utils.logging import setup_logging
237+
from pyssm_client.cli.types import ConnectArguments
238+
from pyssm_client.cli.main import SessionManagerPlugin
239+
from pyssm_client.utils.logging import setup_logging
225240
226241
setup_logging() # or logging.DEBUG for verbose
227242
@@ -241,9 +256,9 @@ exit_code = asyncio.run(plugin.run_session(args))
241256

242257
```
243258
import asyncio
244-
from session_manager_plugin.session.session_handler import SessionHandler
245-
from session_manager_plugin.communicator.utils import create_websocket_config
246-
from session_manager_plugin.communicator.data_channel import SessionDataChannel
259+
from pyssm_client.session.session_handler import SessionHandler
260+
from pyssm_client.communicator.utils import create_websocket_config
261+
from pyssm_client.communicator.data_channel import SessionDataChannel
247262
248263
async def main():
249264
handler = SessionHandler()

0 commit comments

Comments
 (0)