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 ADDED
File without changes
evo_cli/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Entry point for evo_cli."""
2
+
3
+ from evo_cli.cli import main # pragma: no cover
4
+
5
+ if __name__ == "__main__": # pragma: no cover
6
+ main()
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
+ [![codecov](https://codecov.io/gh/maycuatroi/evo-cli/branch/main/graph/badge.svg?token=evo-cli_token_here)](https://codecov.io/gh/maycuatroi/evo-cli)
26
+ [![CI](https://github.com/maycuatroi/evo-cli/actions/workflows/main.yml/badge.svg)](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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.45.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ evo_cli = evo_cli.__main__:main
3
+
@@ -0,0 +1 @@
1
+ evo_cli