evo-cli 0.1.1__py3-none-any.whl
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/__init__.py +0 -0
- evo_cli/__main__.py +6 -0
- evo_cli/base.py +17 -0
- evo_cli/cli.py +61 -0
- evo_cli/ssh_setup.py +212 -0
- evo_cli-0.1.1.dist-info/LICENSE +24 -0
- evo_cli-0.1.1.dist-info/METADATA +84 -0
- evo_cli-0.1.1.dist-info/RECORD +11 -0
- evo_cli-0.1.1.dist-info/WHEEL +5 -0
- evo_cli-0.1.1.dist-info/entry_points.txt +3 -0
- evo_cli-0.1.1.dist-info/top_level.txt +1 -0
evo_cli/__init__.py
ADDED
File without changes
|
evo_cli/__main__.py
ADDED
evo_cli/base.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
"""
|
2
|
+
evo_cli base module.
|
3
|
+
|
4
|
+
This is the principal module of the evo_cli project.
|
5
|
+
here you put your main classes and objects.
|
6
|
+
|
7
|
+
Be creative! do whatever you want!
|
8
|
+
|
9
|
+
If you want to replace this with a Flask application run:
|
10
|
+
|
11
|
+
$ make init
|
12
|
+
|
13
|
+
and then choose `flask` as template.
|
14
|
+
"""
|
15
|
+
|
16
|
+
# example constant variable
|
17
|
+
NAME = "evo_cli"
|
evo_cli/cli.py
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
"""CLI interface for evo_cli project.
|
2
|
+
|
3
|
+
Be creative! do whatever you want!
|
4
|
+
|
5
|
+
- Install click or typer and create a CLI app
|
6
|
+
- Use builtin argparse
|
7
|
+
- Start a web application
|
8
|
+
- Import things from your .base module
|
9
|
+
"""
|
10
|
+
|
11
|
+
import argparse
|
12
|
+
import sys
|
13
|
+
from evo_cli.ssh_setup import setup_ssh, show_usage
|
14
|
+
|
15
|
+
def main(): # pragma: no cover
|
16
|
+
"""
|
17
|
+
The main function executes on commands:
|
18
|
+
`python -m evo_cli` and `$ evo_cli`.
|
19
|
+
|
20
|
+
This is your program's entry point.
|
21
|
+
|
22
|
+
You can change this function to do whatever you want.
|
23
|
+
Examples:
|
24
|
+
* Run a test suite
|
25
|
+
* Run a server
|
26
|
+
* Do some other stuff
|
27
|
+
* Run a command line application (Click, Typer, ArgParse)
|
28
|
+
* List all available tasks
|
29
|
+
* Run an application (Flask, FastAPI, Django, etc.)
|
30
|
+
"""
|
31
|
+
parser = argparse.ArgumentParser(
|
32
|
+
description="EVO CLI - A collection of useful tools",
|
33
|
+
usage="evo <command> [<args>]"
|
34
|
+
)
|
35
|
+
parser.add_argument("command", help="Command to run")
|
36
|
+
|
37
|
+
# Parse just the command argument
|
38
|
+
args = parser.parse_args(sys.argv[1:2])
|
39
|
+
|
40
|
+
# Route to appropriate command handler
|
41
|
+
if args.command == "setupssh":
|
42
|
+
# Parse arguments for ssh setup
|
43
|
+
ssh_parser = argparse.ArgumentParser(description="Set up SSH with key-based authentication")
|
44
|
+
ssh_parser.add_argument('-H', '--host', help='SSH server hostname or IP address')
|
45
|
+
ssh_parser.add_argument('-u', '--user', help='SSH username')
|
46
|
+
ssh_parser.add_argument('-p', '--password', help='SSH password (not recommended, use interactive mode instead)')
|
47
|
+
ssh_parser.add_argument('-i', '--identity', help='Path to existing identity file to use')
|
48
|
+
ssh_parser.add_argument('--help-examples', action='store_true', help='Show usage examples')
|
49
|
+
|
50
|
+
ssh_args = ssh_parser.parse_args(sys.argv[2:])
|
51
|
+
|
52
|
+
if ssh_args.help_examples:
|
53
|
+
show_usage()
|
54
|
+
return
|
55
|
+
|
56
|
+
setup_ssh(ssh_args)
|
57
|
+
else:
|
58
|
+
print(f"Unknown command: {args.command}")
|
59
|
+
print("Available commands:")
|
60
|
+
print(" setupssh - Set up SSH key-based authentication")
|
61
|
+
parser.print_help()
|
evo_cli/ssh_setup.py
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
"""SSH key setup functionality for EVO CLI."""
|
2
|
+
import os
|
3
|
+
import warnings
|
4
|
+
|
5
|
+
# Suppress deprecation warnings before importing paramiko
|
6
|
+
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
7
|
+
try:
|
8
|
+
from cryptography.utils import CryptographyDeprecationWarning
|
9
|
+
warnings.filterwarnings("ignore", category=CryptographyDeprecationWarning)
|
10
|
+
except ImportError:
|
11
|
+
pass
|
12
|
+
|
13
|
+
import paramiko
|
14
|
+
import getpass
|
15
|
+
import sys
|
16
|
+
from pathlib import Path
|
17
|
+
import subprocess
|
18
|
+
import argparse
|
19
|
+
|
20
|
+
def connect_ssh(hostname, username, password):
|
21
|
+
"""Connect to SSH server using provided credentials."""
|
22
|
+
try:
|
23
|
+
# Create SSH client
|
24
|
+
client = paramiko.SSHClient()
|
25
|
+
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
26
|
+
|
27
|
+
# Connect to server
|
28
|
+
print(f"Connecting to {hostname} as {username}...")
|
29
|
+
client.connect(hostname=hostname, username=username, password=password)
|
30
|
+
|
31
|
+
print("Connection successful!")
|
32
|
+
|
33
|
+
# Execute a simple command to verify connection
|
34
|
+
stdin, stdout, stderr = client.exec_command("hostname")
|
35
|
+
result = stdout.read().decode().strip()
|
36
|
+
print(f"Server hostname: {result}")
|
37
|
+
|
38
|
+
return client
|
39
|
+
except Exception as e:
|
40
|
+
print(f"Error connecting to SSH: {str(e)}")
|
41
|
+
return None
|
42
|
+
|
43
|
+
def ensure_ssh_key_exists():
|
44
|
+
"""Generate SSH key if it doesn't exist."""
|
45
|
+
ssh_dir = Path.home() / ".ssh"
|
46
|
+
id_rsa_path = ssh_dir / "id_rsa"
|
47
|
+
id_rsa_pub_path = ssh_dir / "id_rsa.pub"
|
48
|
+
|
49
|
+
# Create .ssh directory if it doesn't exist
|
50
|
+
os.makedirs(ssh_dir, exist_ok=True)
|
51
|
+
|
52
|
+
# Check if SSH key already exists
|
53
|
+
if id_rsa_path.exists() and id_rsa_pub_path.exists():
|
54
|
+
print("SSH key pair already exists.")
|
55
|
+
return id_rsa_path, id_rsa_pub_path
|
56
|
+
|
57
|
+
# Generate SSH key pair
|
58
|
+
print("Generating new SSH key pair...")
|
59
|
+
try:
|
60
|
+
subprocess.run(['ssh-keygen', '-t', 'rsa', '-b', '4096', '-f', str(id_rsa_path), '-N', ''],
|
61
|
+
check=True, capture_output=True)
|
62
|
+
print(f"SSH key pair generated at {id_rsa_path}")
|
63
|
+
return id_rsa_path, id_rsa_pub_path
|
64
|
+
except Exception as e:
|
65
|
+
print(f"Error generating SSH key: {str(e)}")
|
66
|
+
return None, None
|
67
|
+
|
68
|
+
def upload_ssh_key(client, pub_key_path):
|
69
|
+
"""Upload the public key to the remote server."""
|
70
|
+
try:
|
71
|
+
# Read public key content
|
72
|
+
with open(pub_key_path, 'r') as f:
|
73
|
+
pub_key_content = f.read().strip()
|
74
|
+
|
75
|
+
# Create ~/.ssh directory on remote server if it doesn't exist
|
76
|
+
stdin, stdout, stderr = client.exec_command("mkdir -p ~/.ssh")
|
77
|
+
if stderr.read():
|
78
|
+
print(f"Error creating .ssh directory: {stderr.read().decode()}")
|
79
|
+
return False
|
80
|
+
|
81
|
+
# Add the public key to authorized_keys
|
82
|
+
stdin, stdout, stderr = client.exec_command(f"echo '{pub_key_content}' >> ~/.ssh/authorized_keys")
|
83
|
+
if stderr.read():
|
84
|
+
print(f"Error adding public key: {stderr.read().decode()}")
|
85
|
+
return False
|
86
|
+
|
87
|
+
# Set proper permissions
|
88
|
+
stdin, stdout, stderr = client.exec_command("chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys")
|
89
|
+
if stderr.read():
|
90
|
+
print(f"Error setting permissions: {stderr.read().decode()}")
|
91
|
+
return False
|
92
|
+
|
93
|
+
print("SSH public key successfully uploaded to the server.")
|
94
|
+
return True
|
95
|
+
except Exception as e:
|
96
|
+
print(f"Error uploading SSH key: {str(e)}")
|
97
|
+
return False
|
98
|
+
|
99
|
+
def save_to_ssh_config(hostname, username, identity_file):
|
100
|
+
"""Save SSH credentials to ~/.ssh/config file."""
|
101
|
+
try:
|
102
|
+
# Get path to SSH config file
|
103
|
+
ssh_dir = Path.home() / ".ssh"
|
104
|
+
ssh_config_path = ssh_dir / "config"
|
105
|
+
|
106
|
+
# Create .ssh directory if it doesn't exist
|
107
|
+
os.makedirs(ssh_dir, exist_ok=True)
|
108
|
+
|
109
|
+
# Prepare config entry
|
110
|
+
config_entry = f"""
|
111
|
+
Host {hostname}
|
112
|
+
HostName {hostname}
|
113
|
+
User {username}
|
114
|
+
IdentityFile {identity_file}
|
115
|
+
"""
|
116
|
+
|
117
|
+
# Check if file exists and if the host is already configured
|
118
|
+
if ssh_config_path.exists():
|
119
|
+
with open(ssh_config_path, 'r') as f:
|
120
|
+
content = f.read()
|
121
|
+
if f"Host {hostname}" in content:
|
122
|
+
print(f"Host {hostname} already exists in SSH config. Not modifying the file.")
|
123
|
+
return
|
124
|
+
|
125
|
+
# Append to existing file or create new one
|
126
|
+
with open(ssh_config_path, 'a+') as f:
|
127
|
+
f.write(config_entry)
|
128
|
+
|
129
|
+
print(f"SSH config saved to {ssh_config_path}")
|
130
|
+
print("You can now connect without a password using:")
|
131
|
+
print(f" ssh {hostname}")
|
132
|
+
print("Or through VSCode by adding this in your SSH config.")
|
133
|
+
except Exception as e:
|
134
|
+
print(f"Error saving SSH config: {str(e)}")
|
135
|
+
|
136
|
+
def setup_ssh(args=None):
|
137
|
+
"""Main function to set up SSH with key-based authentication."""
|
138
|
+
# Parse command line arguments if provided
|
139
|
+
if args is None:
|
140
|
+
parser = argparse.ArgumentParser(description='Set up SSH with key-based authentication.')
|
141
|
+
parser.add_argument('-H', '--host', help='SSH server hostname or IP address')
|
142
|
+
parser.add_argument('-u', '--user', help='SSH username')
|
143
|
+
parser.add_argument('-p', '--password', help='SSH password (not recommended, use interactive mode instead)')
|
144
|
+
parser.add_argument('-i', '--identity', help='Path to existing identity file to use')
|
145
|
+
args = parser.parse_args()
|
146
|
+
|
147
|
+
# Get credentials from arguments or prompt the user
|
148
|
+
if args.host and args.user and args.password:
|
149
|
+
# Use command line arguments
|
150
|
+
hostname = args.host
|
151
|
+
username = args.user
|
152
|
+
password = args.password
|
153
|
+
print(f"Using provided credentials for {username}@{hostname}")
|
154
|
+
else:
|
155
|
+
# Get user input for credentials
|
156
|
+
print("Enter SSH connection details:")
|
157
|
+
hostname = args.host or input("Hostname/IP: ").strip()
|
158
|
+
username = args.user or input("Username (default: root): ").strip() or "root"
|
159
|
+
password = args.password or getpass.getpass("Password: ")
|
160
|
+
|
161
|
+
if not password:
|
162
|
+
print("Password is required. Exiting.")
|
163
|
+
return
|
164
|
+
|
165
|
+
# Ensure SSH key pair exists or use provided identity
|
166
|
+
if hasattr(args, 'identity') and args.identity:
|
167
|
+
private_key_path = Path(args.identity)
|
168
|
+
public_key_path = Path(f"{args.identity}.pub")
|
169
|
+
if not private_key_path.exists() or not public_key_path.exists():
|
170
|
+
print(f"Specified identity file {args.identity} not found or missing public key.")
|
171
|
+
return
|
172
|
+
print(f"Using existing identity file: {private_key_path}")
|
173
|
+
else:
|
174
|
+
private_key_path, public_key_path = ensure_ssh_key_exists()
|
175
|
+
if not private_key_path or not public_key_path:
|
176
|
+
print("Failed to ensure SSH key exists. Exiting.")
|
177
|
+
return
|
178
|
+
|
179
|
+
# Connect to SSH server
|
180
|
+
client = connect_ssh(hostname, username, password)
|
181
|
+
if not client:
|
182
|
+
print("SSH connection failed. Exiting.")
|
183
|
+
return
|
184
|
+
|
185
|
+
# Upload SSH key to server
|
186
|
+
if upload_ssh_key(client, public_key_path):
|
187
|
+
# Close the connection
|
188
|
+
client.close()
|
189
|
+
# Save to SSH config with identity file
|
190
|
+
save_to_ssh_config(hostname, username, str(private_key_path))
|
191
|
+
else:
|
192
|
+
client.close()
|
193
|
+
print("Failed to set up passwordless authentication.")
|
194
|
+
|
195
|
+
def show_usage():
|
196
|
+
"""Show usage examples."""
|
197
|
+
print("""
|
198
|
+
SSH Key Setup Script
|
199
|
+
===================
|
200
|
+
|
201
|
+
This script helps you set up SSH key-based authentication for password-less login.
|
202
|
+
|
203
|
+
Usage examples:
|
204
|
+
1. Interactive mode:
|
205
|
+
evo setupssh
|
206
|
+
|
207
|
+
2. Command line mode:
|
208
|
+
evo setupssh -H 42.96.16.233 -u root -p YourPassword
|
209
|
+
|
210
|
+
3. Use existing identity file:
|
211
|
+
evo setupssh -H 42.96.16.233 -u root -p YourPassword -i /path/to/private_key
|
212
|
+
""")
|
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <https://unlicense.org>
|
@@ -0,0 +1,84 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: evo-cli
|
3
|
+
Version: 0.1.1
|
4
|
+
Summary: Awesome evo_cli created by maycuatroi
|
5
|
+
Home-page: https://github.com/maycuatroi/evo-cli/
|
6
|
+
Author: maycuatroi
|
7
|
+
License: UNKNOWN
|
8
|
+
Platform: UNKNOWN
|
9
|
+
Description-Content-Type: text/markdown
|
10
|
+
License-File: LICENSE
|
11
|
+
Requires-Dist: paramiko>=2.7.0
|
12
|
+
Provides-Extra: test
|
13
|
+
Requires-Dist: pytest; extra == "test"
|
14
|
+
Requires-Dist: coverage; extra == "test"
|
15
|
+
Requires-Dist: flake8; extra == "test"
|
16
|
+
Requires-Dist: black; extra == "test"
|
17
|
+
Requires-Dist: isort; extra == "test"
|
18
|
+
Requires-Dist: pytest-cov; extra == "test"
|
19
|
+
Requires-Dist: mypy; extra == "test"
|
20
|
+
Requires-Dist: gitchangelog; extra == "test"
|
21
|
+
Requires-Dist: mkdocs; extra == "test"
|
22
|
+
|
23
|
+
# Evolution CLI (Develop by Dev And for Dev)
|
24
|
+
|
25
|
+
[](https://codecov.io/gh/maycuatroi/evo-cli)
|
26
|
+
[](https://github.com/maycuatroi/evo-cli/actions/workflows/main.yml)
|
27
|
+
|
28
|
+
Awesome evo_cli created by maycuatroi
|
29
|
+
|
30
|
+
## Install it from PyPI
|
31
|
+
|
32
|
+
```bash
|
33
|
+
pip install evo_cli
|
34
|
+
```
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
```py
|
39
|
+
from evo_cli import BaseClass
|
40
|
+
from evo_cli import base_function
|
41
|
+
|
42
|
+
BaseClass().base_method()
|
43
|
+
base_function()
|
44
|
+
```
|
45
|
+
|
46
|
+
```bash
|
47
|
+
$ python -m evo_cli
|
48
|
+
#or
|
49
|
+
$ evo_cli
|
50
|
+
```
|
51
|
+
|
52
|
+
## Development
|
53
|
+
|
54
|
+
Read the [CONTRIBUTING.md](CONTRIBUTING.md) file.
|
55
|
+
|
56
|
+
## Automatic Release to PyPI
|
57
|
+
|
58
|
+
This project is configured to automatically release to PyPI when changes are pushed to the main branch. The process:
|
59
|
+
|
60
|
+
1. Automatically bumps the version based on commit messages:
|
61
|
+
- Commits with "BREAKING CHANGE" trigger a major version bump
|
62
|
+
- Commits with "feat" trigger a minor version bump
|
63
|
+
- All other commits trigger a patch version bump
|
64
|
+
|
65
|
+
2. Builds and publishes the package to PyPI
|
66
|
+
|
67
|
+
3. Creates a GitHub release with auto-generated release notes
|
68
|
+
|
69
|
+
### Setup Requirements
|
70
|
+
|
71
|
+
To enable automatic PyPI releases, you need to:
|
72
|
+
|
73
|
+
1. Create a PyPI API token:
|
74
|
+
- Go to https://pypi.org/manage/account/token/
|
75
|
+
- Create a new token with scope "Entire account"
|
76
|
+
- Copy the token value
|
77
|
+
|
78
|
+
2. Add the token to your GitHub repository secrets:
|
79
|
+
- Go to your GitHub repository → Settings → Secrets and variables → Actions
|
80
|
+
- Create a new repository secret named `PYPI_API_TOKEN`
|
81
|
+
- Paste your PyPI token as the value
|
82
|
+
|
83
|
+
After these steps, the automated release process will work whenever changes are pushed to the main branch.
|
84
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
evo_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
evo_cli/__main__.py,sha256=7gjzkdgmHtmiJQKpltZpZ-t26bsBlgXlTzkCIjR0fnk,140
|
3
|
+
evo_cli/base.py,sha256=kHzHVhx4SyKp7GETrpa_QpuYnhX2zGxtkaa0xkwBXNo,323
|
4
|
+
evo_cli/cli.py,sha256=vFQDqDwTK7niNZ-z0PjmEnW7nmdDaB98bqDqeJSHvVg,2158
|
5
|
+
evo_cli/ssh_setup.py,sha256=ii2ii0us0em9aqlVPZmJAC3jPfNEPQFYHwUYUv3_Y0w,7904
|
6
|
+
evo_cli-0.1.1.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
|
7
|
+
evo_cli-0.1.1.dist-info/METADATA,sha256=C3trszSelGZujb8g0O5a2y0nDOUefj6iY9W5wOrSuTQ,2426
|
8
|
+
evo_cli-0.1.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
9
|
+
evo_cli-0.1.1.dist-info/entry_points.txt,sha256=KAho_YevnWKWXCxu0LKp7nIFQtMbqWoxQ8_tvDauojs,51
|
10
|
+
evo_cli-0.1.1.dist-info/top_level.txt,sha256=oZVCrBYcBYaYGLMqkmnBxt_ZPVDb_shL54Q6dgX5fPk,8
|
11
|
+
evo_cli-0.1.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
evo_cli
|