evo-cli 0.1.7__tar.gz → 0.1.9__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.
- {evo_cli-0.1.7 → evo_cli-0.1.9}/PKG-INFO +2 -1
- evo_cli-0.1.9/evo_cli/VERSION +1 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli/cli.py +24 -20
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli/miniconda_setup.py +35 -32
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli/ssh_setup.py +75 -51
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli.egg-info/PKG-INFO +2 -1
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli.egg-info/SOURCES.txt +1 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli.egg-info/requires.txt +1 -0
- evo_cli-0.1.9/setup.cfg +8 -0
- evo_cli-0.1.7/evo_cli/VERSION +0 -1
- evo_cli-0.1.7/setup.cfg +0 -4
- {evo_cli-0.1.7 → evo_cli-0.1.9}/Containerfile +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/HISTORY.md +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/LICENSE +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/MANIFEST.in +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/README.md +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli/__init__.py +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli/__main__.py +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli/base.py +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli.egg-info/dependency_links.txt +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli.egg-info/entry_points.txt +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/evo_cli.egg-info/top_level.txt +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/setup.py +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/tests/__init__.py +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/tests/conftest.py +0 -0
- {evo_cli-0.1.7 → evo_cli-0.1.9}/tests/test_base.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evo_cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Awesome evo_cli created by maycuatroi
|
|
5
5
|
Home-page: https://github.com/maycuatroi/evo-cli/
|
|
6
6
|
Author: maycuatroi
|
|
@@ -15,6 +15,7 @@ Requires-Dist: black; extra == "test"
|
|
|
15
15
|
Requires-Dist: isort; extra == "test"
|
|
16
16
|
Requires-Dist: pytest-cov; extra == "test"
|
|
17
17
|
Requires-Dist: mypy; extra == "test"
|
|
18
|
+
Requires-Dist: types-paramiko; extra == "test"
|
|
18
19
|
Requires-Dist: gitchangelog; extra == "test"
|
|
19
20
|
Requires-Dist: mkdocs; extra == "test"
|
|
20
21
|
Dynamic: author
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.9
|
|
@@ -10,8 +10,12 @@ Be creative! do whatever you want!
|
|
|
10
10
|
|
|
11
11
|
import argparse
|
|
12
12
|
import sys
|
|
13
|
-
|
|
14
|
-
from evo_cli.miniconda_setup import install_miniconda
|
|
13
|
+
|
|
14
|
+
from evo_cli.miniconda_setup import install_miniconda
|
|
15
|
+
from evo_cli.miniconda_setup import show_usage as show_miniconda_usage
|
|
16
|
+
from evo_cli.ssh_setup import setup_ssh
|
|
17
|
+
from evo_cli.ssh_setup import show_usage as show_ssh_usage
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
def main(): # pragma: no cover
|
|
17
21
|
"""
|
|
@@ -30,44 +34,44 @@ def main(): # pragma: no cover
|
|
|
30
34
|
* Run an application (Flask, FastAPI, Django, etc.)
|
|
31
35
|
"""
|
|
32
36
|
parser = argparse.ArgumentParser(
|
|
33
|
-
description="EVO CLI - A collection of useful tools",
|
|
34
|
-
usage="evo <command> [<args>]"
|
|
37
|
+
description="EVO CLI - A collection of useful tools", usage="evo <command> [<args>]"
|
|
35
38
|
)
|
|
36
39
|
parser.add_argument("command", help="Command to run")
|
|
37
|
-
|
|
40
|
+
|
|
38
41
|
# Parse just the command argument
|
|
39
42
|
args = parser.parse_args(sys.argv[1:2])
|
|
40
|
-
|
|
43
|
+
|
|
41
44
|
# Route to appropriate command handler
|
|
42
45
|
if args.command == "setupssh":
|
|
43
46
|
# Parse arguments for ssh setup
|
|
44
47
|
ssh_parser = argparse.ArgumentParser(description="Set up SSH with key-based authentication")
|
|
45
|
-
ssh_parser.add_argument(
|
|
46
|
-
ssh_parser.add_argument(
|
|
47
|
-
ssh_parser.add_argument(
|
|
48
|
-
ssh_parser.add_argument(
|
|
49
|
-
ssh_parser.add_argument(
|
|
50
|
-
|
|
48
|
+
ssh_parser.add_argument("-H", "--host", help="SSH server hostname or IP address")
|
|
49
|
+
ssh_parser.add_argument("-u", "--user", help="SSH username")
|
|
50
|
+
ssh_parser.add_argument("-p", "--password", help="SSH password (not recommended, use interactive mode instead)")
|
|
51
|
+
ssh_parser.add_argument("-P", "--port", type=int, default=22, help="SSH port (default: 22)")
|
|
52
|
+
ssh_parser.add_argument("-i", "--identity", help="Path to existing identity file to use")
|
|
53
|
+
ssh_parser.add_argument("--help-examples", action="store_true", help="Show usage examples")
|
|
54
|
+
|
|
51
55
|
ssh_args = ssh_parser.parse_args(sys.argv[2:])
|
|
52
|
-
|
|
56
|
+
|
|
53
57
|
if ssh_args.help_examples:
|
|
54
58
|
show_ssh_usage()
|
|
55
59
|
return
|
|
56
|
-
|
|
60
|
+
|
|
57
61
|
setup_ssh(ssh_args)
|
|
58
62
|
elif args.command == "miniconda":
|
|
59
63
|
# Parse arguments for miniconda installation
|
|
60
64
|
miniconda_parser = argparse.ArgumentParser(description="Install Miniconda with OS-specific settings")
|
|
61
|
-
miniconda_parser.add_argument(
|
|
62
|
-
miniconda_parser.add_argument(
|
|
63
|
-
miniconda_parser.add_argument(
|
|
64
|
-
|
|
65
|
+
miniconda_parser.add_argument("-p", "--prefix", help="Installation directory")
|
|
66
|
+
miniconda_parser.add_argument("-f", "--force", action="store_true", help="Force reinstallation")
|
|
67
|
+
miniconda_parser.add_argument("--help-examples", action="store_true", help="Show usage examples")
|
|
68
|
+
|
|
65
69
|
miniconda_args = miniconda_parser.parse_args(sys.argv[2:])
|
|
66
|
-
|
|
70
|
+
|
|
67
71
|
if miniconda_args.help_examples:
|
|
68
72
|
show_miniconda_usage()
|
|
69
73
|
return
|
|
70
|
-
|
|
74
|
+
|
|
71
75
|
install_miniconda(miniconda_args)
|
|
72
76
|
else:
|
|
73
77
|
print(f"Unknown command: {args.command}")
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""Miniconda installation functionality for EVO CLI."""
|
|
2
|
+
|
|
2
3
|
import os
|
|
3
4
|
import platform
|
|
4
5
|
import subprocess
|
|
5
6
|
import tempfile
|
|
6
|
-
|
|
7
|
-
from pathlib import Path
|
|
7
|
+
|
|
8
8
|
|
|
9
9
|
def show_usage():
|
|
10
10
|
"""Show usage examples for Miniconda installation."""
|
|
11
|
-
print(
|
|
11
|
+
print(
|
|
12
|
+
"""
|
|
12
13
|
Miniconda Installation Script
|
|
13
14
|
============================
|
|
14
15
|
|
|
@@ -26,17 +27,21 @@ Examples:
|
|
|
26
27
|
evo miniconda # Install with default settings
|
|
27
28
|
evo miniconda -p /custom/path # Install to a custom path
|
|
28
29
|
evo miniconda -f # Force reinstallation
|
|
29
|
-
"""
|
|
30
|
+
"""
|
|
31
|
+
)
|
|
32
|
+
|
|
30
33
|
|
|
31
34
|
def is_windows():
|
|
32
35
|
"""Check if the current OS is Windows."""
|
|
33
36
|
return platform.system() == "Windows"
|
|
34
37
|
|
|
38
|
+
|
|
35
39
|
def is_conda_installed(prefix):
|
|
36
40
|
"""Check if conda is already installed at the specified prefix."""
|
|
37
41
|
conda_executable = os.path.join(prefix, "condabin", "conda.bat" if is_windows() else "conda")
|
|
38
42
|
return os.path.exists(conda_executable)
|
|
39
43
|
|
|
44
|
+
|
|
40
45
|
def get_default_install_path():
|
|
41
46
|
"""Get the default installation path based on the OS."""
|
|
42
47
|
if is_windows():
|
|
@@ -44,6 +49,7 @@ def get_default_install_path():
|
|
|
44
49
|
else:
|
|
45
50
|
return os.path.join(os.path.expanduser("~"), "miniconda3")
|
|
46
51
|
|
|
52
|
+
|
|
47
53
|
def install_miniconda_windows(prefix, force=False):
|
|
48
54
|
"""Install Miniconda on Windows."""
|
|
49
55
|
if is_conda_installed(prefix) and not force:
|
|
@@ -55,36 +61,31 @@ def install_miniconda_windows(prefix, force=False):
|
|
|
55
61
|
# Create temp directory for the installer
|
|
56
62
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
57
63
|
installer_path = os.path.join(temp_dir, "miniconda_installer.exe")
|
|
58
|
-
|
|
64
|
+
|
|
59
65
|
# Download the installer
|
|
60
66
|
print("Downloading Miniconda installer...")
|
|
67
|
+
url = "https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe"
|
|
61
68
|
download_cmd = [
|
|
62
|
-
"powershell",
|
|
63
|
-
"-Command",
|
|
64
|
-
f"Invoke-WebRequest -Uri
|
|
69
|
+
"powershell",
|
|
70
|
+
"-Command",
|
|
71
|
+
f"Invoke-WebRequest -Uri {url} -OutFile {installer_path}",
|
|
65
72
|
]
|
|
66
73
|
subprocess.run(download_cmd, check=True)
|
|
67
|
-
|
|
74
|
+
|
|
68
75
|
# Run the installer silently
|
|
69
76
|
print(f"Installing Miniconda to {prefix}...")
|
|
70
|
-
install_cmd = [
|
|
71
|
-
installer_path,
|
|
72
|
-
"/InstallationType=JustMe",
|
|
73
|
-
"/RegisterPython=0",
|
|
74
|
-
"/S",
|
|
75
|
-
"/D=" + prefix
|
|
76
|
-
]
|
|
77
|
+
install_cmd = [installer_path, "/InstallationType=JustMe", "/RegisterPython=0", "/S", "/D=" + prefix]
|
|
77
78
|
subprocess.run(install_cmd, check=True)
|
|
78
|
-
|
|
79
|
+
|
|
79
80
|
# Add to PATH using setx
|
|
80
81
|
bin_dir = os.path.join(prefix, "Scripts")
|
|
81
82
|
condabin_dir = os.path.join(prefix, "condabin")
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
# Get current PATH
|
|
84
85
|
path_cmd = ["powershell", "-Command", "Write-Output $env:PATH"]
|
|
85
86
|
result = subprocess.run(path_cmd, capture_output=True, text=True, check=True)
|
|
86
87
|
current_path = result.stdout.strip()
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
# Only add to PATH if not already present
|
|
89
90
|
if bin_dir not in current_path and condabin_dir not in current_path:
|
|
90
91
|
print("Adding Miniconda to PATH...")
|
|
@@ -94,11 +95,12 @@ def install_miniconda_windows(prefix, force=False):
|
|
|
94
95
|
print("\nTo use conda, restart your terminal or run:")
|
|
95
96
|
print(f" {os.path.join(prefix, 'condabin', 'conda.bat')} init")
|
|
96
97
|
return True
|
|
97
|
-
|
|
98
|
+
|
|
98
99
|
except Exception as e:
|
|
99
100
|
print(f"Error installing Miniconda: {str(e)}")
|
|
100
101
|
return False
|
|
101
102
|
|
|
103
|
+
|
|
102
104
|
def install_miniconda_unix(prefix, force=False):
|
|
103
105
|
"""Install Miniconda on Linux/macOS."""
|
|
104
106
|
if is_conda_installed(prefix) and not force:
|
|
@@ -110,7 +112,7 @@ def install_miniconda_unix(prefix, force=False):
|
|
|
110
112
|
# Create temp directory for the installer
|
|
111
113
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
112
114
|
installer_path = os.path.join(temp_dir, "miniconda.sh")
|
|
113
|
-
|
|
115
|
+
|
|
114
116
|
# Determine the correct installer based on platform
|
|
115
117
|
if platform.system() == "Darwin":
|
|
116
118
|
if platform.machine() == "arm64":
|
|
@@ -122,52 +124,53 @@ def install_miniconda_unix(prefix, force=False):
|
|
|
122
124
|
installer_url = "https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh"
|
|
123
125
|
else:
|
|
124
126
|
installer_url = "https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh"
|
|
125
|
-
|
|
127
|
+
|
|
126
128
|
# Download the installer
|
|
127
129
|
print("Downloading Miniconda installer...")
|
|
128
130
|
download_cmd = ["wget", installer_url, "-O", installer_path]
|
|
129
131
|
subprocess.run(download_cmd, check=True)
|
|
130
|
-
|
|
132
|
+
|
|
131
133
|
# Make the installer executable
|
|
132
134
|
subprocess.run(["chmod", "+x", installer_path], check=True)
|
|
133
|
-
|
|
135
|
+
|
|
134
136
|
# Run the installer
|
|
135
137
|
print(f"Installing Miniconda to {prefix}...")
|
|
136
138
|
install_cmd = ["bash", installer_path, "-b", "-p", prefix]
|
|
137
139
|
subprocess.run(install_cmd, check=True)
|
|
138
|
-
|
|
140
|
+
|
|
139
141
|
# Add to PATH if needed
|
|
140
142
|
shell_rc_file = os.path.expanduser("~/.bashrc")
|
|
141
143
|
if platform.system() == "Darwin":
|
|
142
144
|
if os.path.exists(os.path.expanduser("~/.zshrc")):
|
|
143
145
|
shell_rc_file = os.path.expanduser("~/.zshrc")
|
|
144
|
-
|
|
146
|
+
|
|
145
147
|
# Check if already in PATH
|
|
146
148
|
with open(shell_rc_file, "r") as f:
|
|
147
149
|
content = f.read()
|
|
148
150
|
if prefix not in content:
|
|
149
151
|
print(f"Adding Miniconda to PATH in {shell_rc_file}...")
|
|
150
152
|
with open(shell_rc_file, "a") as f:
|
|
151
|
-
f.write(
|
|
153
|
+
f.write("\n# >>> conda initialize >>>\n")
|
|
152
154
|
f.write(f'export PATH="{prefix}/bin:$PATH"\n')
|
|
153
|
-
f.write(
|
|
154
|
-
|
|
155
|
+
f.write("# <<< conda initialize <<<\n")
|
|
156
|
+
|
|
155
157
|
print("\nMiniconda has been installed successfully!")
|
|
156
158
|
print("\nTo use conda, restart your terminal or run:")
|
|
157
159
|
print(f" source {shell_rc_file}")
|
|
158
160
|
return True
|
|
159
|
-
|
|
161
|
+
|
|
160
162
|
except Exception as e:
|
|
161
163
|
print(f"Error installing Miniconda: {str(e)}")
|
|
162
164
|
return False
|
|
163
165
|
|
|
166
|
+
|
|
164
167
|
def install_miniconda(args):
|
|
165
168
|
"""Main function to install Miniconda based on the current OS."""
|
|
166
169
|
# Get installation prefix
|
|
167
170
|
prefix = args.prefix or get_default_install_path()
|
|
168
|
-
|
|
171
|
+
|
|
169
172
|
# Install based on OS
|
|
170
173
|
if is_windows():
|
|
171
174
|
return install_miniconda_windows(prefix, args.force)
|
|
172
175
|
else:
|
|
173
|
-
return install_miniconda_unix(prefix, args.force)
|
|
176
|
+
return install_miniconda_unix(prefix, args.force)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""SSH key setup functionality for EVO CLI."""
|
|
2
|
+
|
|
2
3
|
import os
|
|
3
4
|
import warnings
|
|
4
5
|
|
|
@@ -6,126 +7,134 @@ import warnings
|
|
|
6
7
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
7
8
|
try:
|
|
8
9
|
from cryptography.utils import CryptographyDeprecationWarning
|
|
10
|
+
|
|
9
11
|
warnings.filterwarnings("ignore", category=CryptographyDeprecationWarning)
|
|
10
12
|
except ImportError:
|
|
11
13
|
pass
|
|
12
14
|
|
|
13
|
-
import
|
|
14
|
-
import getpass
|
|
15
|
-
import
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
|
|
18
|
-
import
|
|
15
|
+
import argparse # noqa: E402
|
|
16
|
+
import getpass # noqa: E402
|
|
17
|
+
import subprocess # noqa: E402
|
|
18
|
+
from pathlib import Path # noqa: E402
|
|
19
|
+
|
|
20
|
+
import paramiko # noqa: E402
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
def connect_ssh(hostname, username, password, port=22):
|
|
21
24
|
"""Connect to SSH server using provided credentials."""
|
|
22
25
|
try:
|
|
23
26
|
# Create SSH client
|
|
24
27
|
client = paramiko.SSHClient()
|
|
25
28
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
26
|
-
|
|
29
|
+
|
|
27
30
|
# Connect to server
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
port_info = f":{port}" if port != 22 else ""
|
|
32
|
+
print(f"Connecting to {hostname}{port_info} as {username}...")
|
|
33
|
+
client.connect(hostname=hostname, username=username, password=password, port=port)
|
|
34
|
+
|
|
31
35
|
print("Connection successful!")
|
|
32
|
-
|
|
36
|
+
|
|
33
37
|
# Execute a simple command to verify connection
|
|
34
38
|
stdin, stdout, stderr = client.exec_command("hostname")
|
|
35
39
|
result = stdout.read().decode().strip()
|
|
36
40
|
print(f"Server hostname: {result}")
|
|
37
|
-
|
|
41
|
+
|
|
38
42
|
return client
|
|
39
43
|
except Exception as e:
|
|
40
44
|
print(f"Error connecting to SSH: {str(e)}")
|
|
41
45
|
return None
|
|
42
46
|
|
|
47
|
+
|
|
43
48
|
def ensure_ssh_key_exists():
|
|
44
49
|
"""Generate SSH key if it doesn't exist."""
|
|
45
50
|
ssh_dir = Path.home() / ".ssh"
|
|
46
51
|
id_rsa_path = ssh_dir / "id_rsa"
|
|
47
52
|
id_rsa_pub_path = ssh_dir / "id_rsa.pub"
|
|
48
|
-
|
|
53
|
+
|
|
49
54
|
# Create .ssh directory if it doesn't exist
|
|
50
55
|
os.makedirs(ssh_dir, exist_ok=True)
|
|
51
|
-
|
|
56
|
+
|
|
52
57
|
# Check if SSH key already exists
|
|
53
58
|
if id_rsa_path.exists() and id_rsa_pub_path.exists():
|
|
54
59
|
print("SSH key pair already exists.")
|
|
55
60
|
return id_rsa_path, id_rsa_pub_path
|
|
56
|
-
|
|
61
|
+
|
|
57
62
|
# Generate SSH key pair
|
|
58
63
|
print("Generating new SSH key pair...")
|
|
59
64
|
try:
|
|
60
|
-
subprocess.run(
|
|
61
|
-
|
|
65
|
+
subprocess.run(
|
|
66
|
+
["ssh-keygen", "-t", "rsa", "-b", "4096", "-f", str(id_rsa_path), "-N", ""], check=True, capture_output=True
|
|
67
|
+
)
|
|
62
68
|
print(f"SSH key pair generated at {id_rsa_path}")
|
|
63
69
|
return id_rsa_path, id_rsa_pub_path
|
|
64
70
|
except Exception as e:
|
|
65
71
|
print(f"Error generating SSH key: {str(e)}")
|
|
66
72
|
return None, None
|
|
67
73
|
|
|
74
|
+
|
|
68
75
|
def upload_ssh_key(client, pub_key_path):
|
|
69
76
|
"""Upload the public key to the remote server."""
|
|
70
77
|
try:
|
|
71
78
|
# Read public key content
|
|
72
|
-
with open(pub_key_path,
|
|
79
|
+
with open(pub_key_path, "r") as f:
|
|
73
80
|
pub_key_content = f.read().strip()
|
|
74
|
-
|
|
81
|
+
|
|
75
82
|
# Create ~/.ssh directory on remote server if it doesn't exist
|
|
76
83
|
stdin, stdout, stderr = client.exec_command("mkdir -p ~/.ssh")
|
|
77
84
|
if stderr.read():
|
|
78
85
|
print(f"Error creating .ssh directory: {stderr.read().decode()}")
|
|
79
86
|
return False
|
|
80
|
-
|
|
87
|
+
|
|
81
88
|
# Add the public key to authorized_keys
|
|
82
89
|
stdin, stdout, stderr = client.exec_command(f"echo '{pub_key_content}' >> ~/.ssh/authorized_keys")
|
|
83
90
|
if stderr.read():
|
|
84
91
|
print(f"Error adding public key: {stderr.read().decode()}")
|
|
85
92
|
return False
|
|
86
|
-
|
|
93
|
+
|
|
87
94
|
# Set proper permissions
|
|
88
95
|
stdin, stdout, stderr = client.exec_command("chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys")
|
|
89
96
|
if stderr.read():
|
|
90
97
|
print(f"Error setting permissions: {stderr.read().decode()}")
|
|
91
98
|
return False
|
|
92
|
-
|
|
99
|
+
|
|
93
100
|
print("SSH public key successfully uploaded to the server.")
|
|
94
101
|
return True
|
|
95
102
|
except Exception as e:
|
|
96
103
|
print(f"Error uploading SSH key: {str(e)}")
|
|
97
104
|
return False
|
|
98
105
|
|
|
99
|
-
|
|
106
|
+
|
|
107
|
+
def save_to_ssh_config(hostname, username, identity_file, port=22):
|
|
100
108
|
"""Save SSH credentials to ~/.ssh/config file."""
|
|
101
109
|
try:
|
|
102
110
|
# Get path to SSH config file
|
|
103
111
|
ssh_dir = Path.home() / ".ssh"
|
|
104
112
|
ssh_config_path = ssh_dir / "config"
|
|
105
|
-
|
|
113
|
+
|
|
106
114
|
# Create .ssh directory if it doesn't exist
|
|
107
115
|
os.makedirs(ssh_dir, exist_ok=True)
|
|
108
|
-
|
|
116
|
+
|
|
109
117
|
# Prepare config entry
|
|
118
|
+
port_line = f" Port {port}\n" if port != 22 else ""
|
|
110
119
|
config_entry = f"""
|
|
111
120
|
Host {hostname}
|
|
112
121
|
HostName {hostname}
|
|
113
|
-
User {username}
|
|
122
|
+
{port_line} User {username}
|
|
114
123
|
IdentityFile {identity_file}
|
|
115
124
|
"""
|
|
116
|
-
|
|
125
|
+
|
|
117
126
|
# Check if file exists and if the host is already configured
|
|
118
127
|
if ssh_config_path.exists():
|
|
119
|
-
with open(ssh_config_path,
|
|
128
|
+
with open(ssh_config_path, "r") as f:
|
|
120
129
|
content = f.read()
|
|
121
130
|
if f"Host {hostname}" in content:
|
|
122
131
|
print(f"Host {hostname} already exists in SSH config. Not modifying the file.")
|
|
123
132
|
return
|
|
124
|
-
|
|
133
|
+
|
|
125
134
|
# Append to existing file or create new one
|
|
126
|
-
with open(ssh_config_path,
|
|
135
|
+
with open(ssh_config_path, "a+") as f:
|
|
127
136
|
f.write(config_entry)
|
|
128
|
-
|
|
137
|
+
|
|
129
138
|
print(f"SSH config saved to {ssh_config_path}")
|
|
130
139
|
print("You can now connect without a password using:")
|
|
131
140
|
print(f" ssh {hostname}")
|
|
@@ -133,37 +142,43 @@ Host {hostname}
|
|
|
133
142
|
except Exception as e:
|
|
134
143
|
print(f"Error saving SSH config: {str(e)}")
|
|
135
144
|
|
|
145
|
+
|
|
136
146
|
def setup_ssh(args=None):
|
|
137
147
|
"""Main function to set up SSH with key-based authentication."""
|
|
138
148
|
# Parse command line arguments if provided
|
|
139
149
|
if args is None:
|
|
140
|
-
parser = argparse.ArgumentParser(description=
|
|
141
|
-
parser.add_argument(
|
|
142
|
-
parser.add_argument(
|
|
143
|
-
parser.add_argument(
|
|
144
|
-
parser.add_argument(
|
|
150
|
+
parser = argparse.ArgumentParser(description="Set up SSH with key-based authentication.")
|
|
151
|
+
parser.add_argument("-H", "--host", help="SSH server hostname or IP address")
|
|
152
|
+
parser.add_argument("-u", "--user", help="SSH username")
|
|
153
|
+
parser.add_argument("-p", "--password", help="SSH password (not recommended, use interactive mode instead)")
|
|
154
|
+
parser.add_argument("-P", "--port", type=int, default=22, help="SSH port (default: 22)")
|
|
155
|
+
parser.add_argument("-i", "--identity", help="Path to existing identity file to use")
|
|
145
156
|
args = parser.parse_args()
|
|
146
|
-
|
|
157
|
+
|
|
158
|
+
# Get port from args (default to 22)
|
|
159
|
+
port = getattr(args, "port", 22) or 22
|
|
160
|
+
|
|
147
161
|
# Get credentials from arguments or prompt the user
|
|
148
162
|
if args.host and args.user and args.password:
|
|
149
163
|
# Use command line arguments
|
|
150
164
|
hostname = args.host
|
|
151
165
|
username = args.user
|
|
152
166
|
password = args.password
|
|
153
|
-
|
|
167
|
+
port_info = f":{port}" if port != 22 else ""
|
|
168
|
+
print(f"Using provided credentials for {username}@{hostname}{port_info}")
|
|
154
169
|
else:
|
|
155
170
|
# Get user input for credentials
|
|
156
171
|
print("Enter SSH connection details:")
|
|
157
172
|
hostname = args.host or input("Hostname/IP: ").strip()
|
|
158
173
|
username = args.user or input("Username (default: root): ").strip() or "root"
|
|
159
174
|
password = args.password or getpass.getpass("Password: ")
|
|
160
|
-
|
|
175
|
+
|
|
161
176
|
if not password:
|
|
162
177
|
print("Password is required. Exiting.")
|
|
163
178
|
return
|
|
164
|
-
|
|
179
|
+
|
|
165
180
|
# Ensure SSH key pair exists or use provided identity
|
|
166
|
-
if hasattr(args,
|
|
181
|
+
if hasattr(args, "identity") and args.identity:
|
|
167
182
|
private_key_path = Path(args.identity)
|
|
168
183
|
public_key_path = Path(f"{args.identity}.pub")
|
|
169
184
|
if not private_key_path.exists() or not public_key_path.exists():
|
|
@@ -175,26 +190,28 @@ def setup_ssh(args=None):
|
|
|
175
190
|
if not private_key_path or not public_key_path:
|
|
176
191
|
print("Failed to ensure SSH key exists. Exiting.")
|
|
177
192
|
return
|
|
178
|
-
|
|
193
|
+
|
|
179
194
|
# Connect to SSH server
|
|
180
|
-
client = connect_ssh(hostname, username, password)
|
|
195
|
+
client = connect_ssh(hostname, username, password, port)
|
|
181
196
|
if not client:
|
|
182
197
|
print("SSH connection failed. Exiting.")
|
|
183
198
|
return
|
|
184
|
-
|
|
199
|
+
|
|
185
200
|
# Upload SSH key to server
|
|
186
201
|
if upload_ssh_key(client, public_key_path):
|
|
187
202
|
# Close the connection
|
|
188
203
|
client.close()
|
|
189
204
|
# Save to SSH config with identity file
|
|
190
|
-
save_to_ssh_config(hostname, username, str(private_key_path))
|
|
205
|
+
save_to_ssh_config(hostname, username, str(private_key_path), port)
|
|
191
206
|
else:
|
|
192
207
|
client.close()
|
|
193
208
|
print("Failed to set up passwordless authentication.")
|
|
194
209
|
|
|
210
|
+
|
|
195
211
|
def show_usage():
|
|
196
212
|
"""Show usage examples."""
|
|
197
|
-
print(
|
|
213
|
+
print(
|
|
214
|
+
"""
|
|
198
215
|
SSH Key Setup Script
|
|
199
216
|
===================
|
|
200
217
|
|
|
@@ -203,10 +220,17 @@ This script helps you set up SSH key-based authentication for password-less logi
|
|
|
203
220
|
Usage examples:
|
|
204
221
|
1. Interactive mode:
|
|
205
222
|
evo setupssh
|
|
206
|
-
|
|
223
|
+
|
|
207
224
|
2. Command line mode:
|
|
208
225
|
evo setupssh -H 42.96.16.233 -u root -p YourPassword
|
|
209
|
-
|
|
210
|
-
3. Use
|
|
226
|
+
|
|
227
|
+
3. Use custom SSH port:
|
|
228
|
+
evo setupssh -H 42.96.16.233 -u root -p YourPassword -P 2222
|
|
229
|
+
|
|
230
|
+
4. Use existing identity file:
|
|
211
231
|
evo setupssh -H 42.96.16.233 -u root -p YourPassword -i /path/to/private_key
|
|
212
|
-
|
|
232
|
+
|
|
233
|
+
5. Use custom port with existing identity file:
|
|
234
|
+
evo setupssh -H 42.96.16.233 -u root -p YourPassword -P 2222 -i /path/to/private_key
|
|
235
|
+
"""
|
|
236
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evo_cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Awesome evo_cli created by maycuatroi
|
|
5
5
|
Home-page: https://github.com/maycuatroi/evo-cli/
|
|
6
6
|
Author: maycuatroi
|
|
@@ -15,6 +15,7 @@ Requires-Dist: black; extra == "test"
|
|
|
15
15
|
Requires-Dist: isort; extra == "test"
|
|
16
16
|
Requires-Dist: pytest-cov; extra == "test"
|
|
17
17
|
Requires-Dist: mypy; extra == "test"
|
|
18
|
+
Requires-Dist: types-paramiko; extra == "test"
|
|
18
19
|
Requires-Dist: gitchangelog; extra == "test"
|
|
19
20
|
Requires-Dist: mkdocs; extra == "test"
|
|
20
21
|
Dynamic: author
|
evo_cli-0.1.9/setup.cfg
ADDED
evo_cli-0.1.7/evo_cli/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.1.7
|
evo_cli-0.1.7/setup.cfg
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|