Skip to content

Commit e3563f4

Browse files
committed
releasing v0.2.0
1 parent 73ab473 commit e3563f4

2 files changed

Lines changed: 57 additions & 28 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "ssh-switch"
7-
version = "0.1.0"
7+
version = "0.2.0"
88
description = "A command-line tool for switching SSH keys"
99
readme = "README.md"
1010
authors = [
@@ -14,7 +14,7 @@ license = { text = "MIT" }
1414
dependencies = [
1515
"questionary"
1616
]
17-
requires-python = ">=3.6"
17+
requires-python = ">=3.0"
1818

1919
[project.urls]
2020
Homepage = "https://github.com/mramkumar/ssh-switch"

ssh_switch/main.py

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
#!/usr/bin/env python3
22

3+
"""
4+
ssh-switch: A CLI tool to manage SSH keys and switch between them easily.
5+
6+
This script provides functionalities for listing SSH keys, switching between them,
7+
and managing the ssh-agent environment.
8+
"""
9+
310
import os
11+
import sys
412
import re
513
import stat
614
import subprocess
715
import platform
816
from pathlib import Path
9-
import questionary
1017
from collections import OrderedDict
18+
import questionary
1119

1220
SSH_DIR = Path.home() / ".ssh"
1321
RECENT_KEYS_FILE = Path.home() / ".ssh_recent_keys"
@@ -16,12 +24,17 @@
1624
BASHRC_FILE = Path.home() / ".bashrc"
1725
ZSHRC_FILE = Path.home() / ".zshrc"
1826

19-
max_keys_per_page = 5
27+
MAX_KEYS_PER_PAGE = 5
28+
ACTIVATION_MESSAGE = False
2029

2130
def get_os_shell_profile():
2231
"""Return the correct shell profile file based on OS."""
2332
return ZSHRC_FILE if platform.system() == "Darwin" else BASHRC_FILE
2433

34+
def print_ACTIVATION_MESSAGE():
35+
"""Prints a message to activate the SSH agent environment."""
36+
print(f"\n 👉 SSH agent is not correctly configured in the terminal session. To fix this, run:\n")
37+
print(f"\033[1;32m source {get_os_shell_profile()} \033[0m\n")
2538

2639
def ensure_ssh_agent_auto_start():
2740
"""Ensure SSH agent environment is auto-loaded in shell profile."""
@@ -39,33 +52,45 @@ def ensure_ssh_agent_auto_start():
3952
print(f"✅ Added SSH agent auto-start snippet to {shell_profile}")
4053

4154
def is_ssh_agent_running():
42-
"""Check if ssh-agent is running and export environment variables."""
55+
56+
global ACTIVATION_MESSAGE
57+
58+
"""Check if ssh-agent is running and export environment variables if needed."""
4359
if not SSH_ENV_FILE.exists():
4460
return False
4561

4662
with SSH_ENV_FILE.open("r") as f:
4763
env_lines = f.readlines()
4864

49-
ssh_auth_sock, ssh_agent_pid = None, None
65+
file_ssh_auth_sock, file_ssh_agent_pid = None, None
5066

5167
for line in env_lines:
5268
if "SSH_AUTH_SOCK" in line:
53-
ssh_auth_sock = line.strip().split("=")[1].replace('"', "")
69+
file_ssh_auth_sock = line.strip().split("=")[1].replace('"', "")
5470
if "SSH_AGENT_PID" in line:
55-
ssh_agent_pid = line.strip().split("=")[1].replace('"', "")
71+
file_ssh_agent_pid = line.strip().split("=")[1].replace('"', "")
5672

57-
if not ssh_auth_sock or not ssh_agent_pid:
73+
if not file_ssh_auth_sock or not file_ssh_agent_pid:
5874
return False
5975

60-
# Verify if the SSH_AGENT_PID is still running
61-
if subprocess.run(["ps", "-p", ssh_agent_pid], capture_output=True).returncode == 0:
62-
os.environ["SSH_AUTH_SOCK"] = ssh_auth_sock
63-
os.environ["SSH_AGENT_PID"] = ssh_agent_pid
64-
return True
76+
# Get current terminal environment variables
77+
env_ssh_auth_sock = os.environ.get("SSH_AUTH_SOCK")
78+
env_ssh_agent_pid = os.environ.get("SSH_AGENT_PID")
79+
80+
# Check if the agent process is running
81+
if subprocess.run(["ps", "-p", file_ssh_agent_pid], capture_output=True).returncode == 0:
82+
# If the values in the environment and file differ, update them
83+
if file_ssh_auth_sock != env_ssh_auth_sock or file_ssh_agent_pid != env_ssh_agent_pid:
84+
ACTIVATION_MESSAGE = True
85+
86+
return True # Agent is running and environment is up to date
6587

6688
return False # Agent is not running, so we need to start a new one
6789

6890
def start_ssh_agent():
91+
92+
global ACTIVATION_MESSAGE
93+
6994
"""Start ssh-agent only if it's not already running."""
7095
if is_ssh_agent_running():
7196
print("✅ ssh-agent is already running.")
@@ -88,10 +113,9 @@ def start_ssh_agent():
88113
print("✅ ssh-agent started successfully.")
89114
else:
90115
print("❌ Failed to start ssh-agent.")
91-
exit(1)
116+
sys.exit(1)
92117

93-
print("\n👉 To activate the SSH agent environment, run:\n")
94-
print(f"\033[1;32m source {get_os_shell_profile()} \033[0m\n")
118+
ACTIVATION_MESSAGE = True
95119

96120
def list_ssh_keys():
97121
"""List all valid SSH private keys in ~/.ssh."""
@@ -100,10 +124,11 @@ def list_ssh_keys():
100124
if file.is_file()
101125
and not file.suffix
102126
and file.stat().st_mode & (stat.S_IRWXG | stat.S_IRWXO) == 0
103-
and subprocess.run(["ssh-keygen", "-y", "-f", str(file)], check=False, capture_output=True).returncode == 0
127+
and subprocess.run(
128+
["ssh-keygen", "-y", "-f", str(file)], check=False, capture_output=True
129+
).returncode == 0
104130
)
105131

106-
107132
def load_recent_keys():
108133
"""Load recently used SSH keys from a file as strings."""
109134
if RECENT_KEYS_FILE.exists():
@@ -120,30 +145,33 @@ def save_recent_key(key_path):
120145
recent_keys.remove(key_path) # Move it to the top
121146

122147
recent_keys.insert(0, key_path) # Add as most recent
123-
recent_keys = recent_keys[:max_keys_per_page] # Keep only the last 5 keys
148+
recent_keys = recent_keys[:MAX_KEYS_PER_PAGE] # Keep only the last 5 keys
124149

125150
with open(RECENT_KEYS_FILE, "w") as f:
126151
f.writelines(f"{key}\n" for key in recent_keys)
127152

128153

129154
def switch_ssh_key(key_path):
155+
156+
global ACTIVATION_MESSAGE
157+
130158
"""Switch SSH key by removing old keys and adding the new key."""
131159
if not is_ssh_agent_running():
132160
print("❌ ssh-agent is not running.")
133-
print("\n👉 To activate the SSH agent environment, run:\n")
134-
print(f"\033[1;32m source {get_os_shell_profile()} \033[0m\n")
161+
ACTIVATION_MESSAGE = True
135162
return
136163

137164
subprocess.run(["ssh-add", "-D"], check=False, capture_output=True)
138-
result = subprocess.run(["ssh-add", str(Path(key_path).resolve())], check=False, capture_output=True)
165+
result = subprocess.run(
166+
["ssh-add", str(Path(key_path).resolve())], check=False, capture_output=True
167+
)
139168

140169
if result.returncode == 0:
141170
print(f"✅ Switched to SSH key: {key_path}")
142171
save_recent_key(key_path)
143172
else:
144173
print(f"❌ Failed to add SSH key: {result.stderr.strip()}")
145174

146-
147175
def verify_ssh_agent():
148176
"""Check if ssh-agent is working correctly."""
149177
result = subprocess.run(["ssh-add", "-l"], check=False, text=True, capture_output=True)
@@ -182,11 +210,10 @@ def interactive_key_selection():
182210

183211
# Pagination setup
184212
page = 0
185-
total_pages = (len(key_list) + max_keys_per_page - 1) // max_keys_per_page # Total pages
186213

187214
while True:
188-
start = page * max_keys_per_page
189-
end = start + max_keys_per_page
215+
start = page * MAX_KEYS_PER_PAGE
216+
end = start + MAX_KEYS_PER_PAGE
190217
display_keys = key_list[start:end]
191218

192219
choices = [
@@ -222,6 +249,8 @@ def main():
222249
else:
223250
print("❌ No key selected. Exiting.")
224251

252+
if ACTIVATION_MESSAGE:
253+
print_ACTIVATION_MESSAGE()
225254

226255
if __name__ == "__main__":
227-
main()
256+
main()

0 commit comments

Comments
 (0)