helper-cli 0.1.11__py3-none-any.whl → 0.1.21__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.
- helper/__init__.py +7 -0
- helper/commands/arch.py +8 -2
- helper/commands/docker.py +378 -210
- helper/commands/file.py +202 -0
- helper/commands/internal_ip.py +10 -2
- helper/commands/nixos.py +57 -31
- helper/commands/public_ip.py +11 -2
- helper/commands/speed.py +38 -0
- helper/commands/system_info.py +326 -0
- helper/commands/venv.py +130 -0
- helper/main.py +68 -27
- helper/table.py +29 -0
- helper_cli-0.1.21.dist-info/METADATA +61 -0
- helper_cli-0.1.21.dist-info/RECORD +19 -0
- {helper_cli-0.1.11.dist-info → helper_cli-0.1.21.dist-info}/entry_points.txt +1 -0
- helper_cli-0.1.11.dist-info/METADATA +0 -6
- helper_cli-0.1.11.dist-info/RECORD +0 -13
- {helper_cli-0.1.11.dist-info → helper_cli-0.1.21.dist-info}/WHEEL +0 -0
- {helper_cli-0.1.11.dist-info → helper_cli-0.1.21.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import platform
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def run_command(cmd):
|
|
9
|
+
"""Run a shell command and return its output"""
|
|
10
|
+
try:
|
|
11
|
+
result = subprocess.check_output(
|
|
12
|
+
cmd, shell=True, text=True, stderr=subprocess.STDOUT
|
|
13
|
+
).strip()
|
|
14
|
+
return result if result else "N/A"
|
|
15
|
+
except subprocess.CalledProcessError as e:
|
|
16
|
+
return f"Error: {e.output}" if e.output else "Command failed"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def format_bytes(size_bytes):
|
|
20
|
+
"""Convert bytes to human readable format"""
|
|
21
|
+
if not isinstance(size_bytes, (int, float)):
|
|
22
|
+
try:
|
|
23
|
+
size_bytes = float(size_bytes)
|
|
24
|
+
except (ValueError, TypeError):
|
|
25
|
+
return str(size_bytes)
|
|
26
|
+
|
|
27
|
+
for unit in ["B", "KB", "MB", "GB", "TB", "PB"]:
|
|
28
|
+
if size_bytes < 1024.0 or unit == "PB":
|
|
29
|
+
return f"{size_bytes:.1f} {unit}"
|
|
30
|
+
size_bytes /= 1024.0
|
|
31
|
+
return f"{size_bytes:.1f} PB"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def parse_vm_stat(output):
|
|
35
|
+
"""Parse macOS vm_stat output and return human readable memory info"""
|
|
36
|
+
if not output or "Mach Virtual Memory Statistics" not in output:
|
|
37
|
+
return "Memory information not available"
|
|
38
|
+
|
|
39
|
+
# Get physical memory using sysctl
|
|
40
|
+
try:
|
|
41
|
+
total_memory = int(run_command("sysctl -n hw.memsize"))
|
|
42
|
+
except (ValueError, subprocess.CalledProcessError):
|
|
43
|
+
return "Error: Could not determine total physical memory"
|
|
44
|
+
|
|
45
|
+
lines = output.split("\n")
|
|
46
|
+
mem_info = {}
|
|
47
|
+
|
|
48
|
+
for line in lines[1:]: # Skip header
|
|
49
|
+
if ":" in line:
|
|
50
|
+
key, value = line.split(":", 1)
|
|
51
|
+
key = key.strip().strip(".")
|
|
52
|
+
try:
|
|
53
|
+
# Convert pages to bytes (1 page = 4KB on macOS)
|
|
54
|
+
pages = int(value.strip().strip("."))
|
|
55
|
+
mem_info[key] = pages * 4096 # 4KB per page
|
|
56
|
+
except (ValueError, AttributeError):
|
|
57
|
+
mem_info[key] = value.strip()
|
|
58
|
+
|
|
59
|
+
# Calculate memory usage
|
|
60
|
+
wired_memory = mem_info.get("Pages wired down", 0)
|
|
61
|
+
active_memory = mem_info.get("Pages active", 0)
|
|
62
|
+
inactive_memory = mem_info.get("Pages inactive", 0)
|
|
63
|
+
free_memory = mem_info.get("Pages free", 0)
|
|
64
|
+
|
|
65
|
+
# Calculate used memory (wired + active)
|
|
66
|
+
used_memory = wired_memory + active_memory
|
|
67
|
+
|
|
68
|
+
# Calculate app memory (active + inactive)
|
|
69
|
+
app_memory = active_memory + inactive_memory
|
|
70
|
+
|
|
71
|
+
# Calculate cached files (inactive memory can be purged by the OS)
|
|
72
|
+
cached_files = inactive_memory
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
f"Total: {format_bytes(total_memory)}\n"
|
|
76
|
+
f"Used: {format_bytes(used_memory)} (Apps: {format_bytes(app_memory)}, Wired: {format_bytes(wired_memory)})\n"
|
|
77
|
+
f"Free: {format_bytes(free_memory)}\n"
|
|
78
|
+
f"Cached: {format_bytes(cached_files)}\n"
|
|
79
|
+
f"Usage: {used_memory/total_memory*100:.1f}%"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def parse_df_output(output):
|
|
84
|
+
"""Parse df output and format sizes"""
|
|
85
|
+
if not output:
|
|
86
|
+
return "Disk information not available"
|
|
87
|
+
|
|
88
|
+
lines = output.split("\n")
|
|
89
|
+
if not lines:
|
|
90
|
+
return output
|
|
91
|
+
|
|
92
|
+
# Keep the header
|
|
93
|
+
result = [lines[0]]
|
|
94
|
+
|
|
95
|
+
# Process each line
|
|
96
|
+
for line in lines[1:]:
|
|
97
|
+
if not line.strip():
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
parts = line.split()
|
|
101
|
+
if len(parts) >= 5:
|
|
102
|
+
# Format size columns (assuming standard df -h output)
|
|
103
|
+
parts[1] = format_bytes(
|
|
104
|
+
parts[1]
|
|
105
|
+
.upper()
|
|
106
|
+
.replace("G", "GB")
|
|
107
|
+
.replace("M", "MB")
|
|
108
|
+
.replace("K", "KB")
|
|
109
|
+
.replace("B", "")
|
|
110
|
+
.replace("I", "")
|
|
111
|
+
.strip()
|
|
112
|
+
)
|
|
113
|
+
parts[2] = format_bytes(
|
|
114
|
+
parts[2]
|
|
115
|
+
.upper()
|
|
116
|
+
.replace("G", "GB")
|
|
117
|
+
.replace("M", "MB")
|
|
118
|
+
.replace("K", "KB")
|
|
119
|
+
.replace("B", "")
|
|
120
|
+
.replace("I", "")
|
|
121
|
+
.strip()
|
|
122
|
+
)
|
|
123
|
+
parts[3] = format_bytes(
|
|
124
|
+
parts[3]
|
|
125
|
+
.upper()
|
|
126
|
+
.replace("G", "GB")
|
|
127
|
+
.replace("M", "MB")
|
|
128
|
+
.replace("K", "KB")
|
|
129
|
+
.replace("B", "")
|
|
130
|
+
.replace("I", "")
|
|
131
|
+
.strip()
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Reconstruct the line with formatted sizes
|
|
135
|
+
result.append(" ".join(parts))
|
|
136
|
+
else:
|
|
137
|
+
result.append(line)
|
|
138
|
+
|
|
139
|
+
return "\n".join(result)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def get_os_specific_info():
|
|
143
|
+
"""Get OS-specific system information"""
|
|
144
|
+
system = platform.system().lower()
|
|
145
|
+
|
|
146
|
+
if system == "darwin": # macOS
|
|
147
|
+
return {
|
|
148
|
+
"cpu": "sysctl -n machdep.cpu.brand_string",
|
|
149
|
+
"cpu_cores": "sysctl -n hw.ncpu",
|
|
150
|
+
"memory": "vm_stat",
|
|
151
|
+
"disks": "df -h",
|
|
152
|
+
"os_version": "sw_vers",
|
|
153
|
+
"hostname": "hostname",
|
|
154
|
+
"uptime": "uptime",
|
|
155
|
+
}
|
|
156
|
+
elif system == "linux":
|
|
157
|
+
return {
|
|
158
|
+
"cpu": 'cat /proc/cpuinfo | grep "model name" | head -n 1 | cut -d":" -f2',
|
|
159
|
+
"cpu_cores": "nproc",
|
|
160
|
+
"memory": "free -h",
|
|
161
|
+
"disks": "df -h",
|
|
162
|
+
"os_version": "cat /etc/os-release",
|
|
163
|
+
"hostname": "hostname",
|
|
164
|
+
"uptime": "uptime",
|
|
165
|
+
}
|
|
166
|
+
elif system == "windows":
|
|
167
|
+
return {
|
|
168
|
+
"cpu": "wmic cpu get name",
|
|
169
|
+
"cpu_cores": "wmic cpu get NumberOfCores",
|
|
170
|
+
"memory": "wmic OS get TotalVisibleMemorySize,FreePhysicalMemory /Value",
|
|
171
|
+
"disks": "wmic logicaldisk get size,freespace,caption",
|
|
172
|
+
"os_version": 'systeminfo | findstr /B /C:"OS Name" /C:"OS Version"',
|
|
173
|
+
"hostname": "hostname",
|
|
174
|
+
"uptime": "wmic os get lastbootuptime",
|
|
175
|
+
}
|
|
176
|
+
else:
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def format_uptime(uptime_str, system):
|
|
181
|
+
"""Format uptime string based on OS"""
|
|
182
|
+
if system == "darwin" or system == "linux":
|
|
183
|
+
# Example: ' 8:53 up 1 day, 3:45, 2 users, load averages: 2.22 2.41 2.35'
|
|
184
|
+
parts = uptime_str.split(",")
|
|
185
|
+
if "up" in parts[0]:
|
|
186
|
+
return "Uptime: " + parts[0].split("up", 1)[1].strip()
|
|
187
|
+
return uptime_str
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@click.command()
|
|
191
|
+
def system_info():
|
|
192
|
+
"""Display system information including CPU, RAM, and disk usage"""
|
|
193
|
+
system = platform.system().lower()
|
|
194
|
+
commands = get_os_specific_info()
|
|
195
|
+
|
|
196
|
+
if not commands:
|
|
197
|
+
click.echo("Unsupported operating system")
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
# System Information
|
|
201
|
+
click.echo("=" * 40 + " System Information " + "=" * 40)
|
|
202
|
+
click.echo(f"System: {platform.system()} {platform.release()}")
|
|
203
|
+
click.echo(f"Node Name: {run_command(commands['hostname'])}")
|
|
204
|
+
click.echo(f"Machine: {platform.machine()}")
|
|
205
|
+
click.echo(
|
|
206
|
+
f"Processor: {platform.processor() or run_command(commands['cpu']).strip()}"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# OS Version
|
|
210
|
+
click.echo("\n" + "=" * 40 + " OS Version " + "=" * 40)
|
|
211
|
+
click.echo(run_command(commands["os_version"]))
|
|
212
|
+
|
|
213
|
+
# Uptime
|
|
214
|
+
click.echo("\n" + "=" * 40 + " Uptime " + "=" * 40)
|
|
215
|
+
uptime = run_command(commands["uptime"])
|
|
216
|
+
click.echo(format_uptime(uptime, system))
|
|
217
|
+
|
|
218
|
+
# CPU Information
|
|
219
|
+
click.echo("\n" + "=" * 40 + " CPU Info " + "=" * 40)
|
|
220
|
+
cpu_cores = run_command(commands["cpu_cores"]).strip()
|
|
221
|
+
click.echo(f"CPU Cores: {cpu_cores}")
|
|
222
|
+
|
|
223
|
+
if system == "darwin" or system == "linux":
|
|
224
|
+
cpu_info = run_command(commands["cpu"]).strip()
|
|
225
|
+
click.echo(f"CPU: {cpu_info}")
|
|
226
|
+
|
|
227
|
+
# CPU Usage (simplified for cross-platform)
|
|
228
|
+
if system == "darwin":
|
|
229
|
+
load_avg = run_command("sysctl -n vm.loadavg").strip()
|
|
230
|
+
click.echo(f"Load Average: {load_avg}")
|
|
231
|
+
elif system == "linux":
|
|
232
|
+
load_avg = run_command("cat /proc/loadavg").strip()
|
|
233
|
+
click.echo(f"Load Average: {load_avg}")
|
|
234
|
+
|
|
235
|
+
# Memory Information
|
|
236
|
+
click.echo("\n" + "=" * 40 + " Memory Information " + "=" * 40)
|
|
237
|
+
if system == "darwin":
|
|
238
|
+
mem_info = run_command("vm_stat")
|
|
239
|
+
click.echo(parse_vm_stat(mem_info))
|
|
240
|
+
elif system == "linux":
|
|
241
|
+
mem_info = run_command("free -b") # Get bytes for consistent formatting
|
|
242
|
+
lines = mem_info.split("\n")
|
|
243
|
+
if len(lines) > 1:
|
|
244
|
+
headers = lines[0].split()
|
|
245
|
+
values = lines[1].split()
|
|
246
|
+
if len(values) >= 7: # For Mem: line
|
|
247
|
+
total = int(values[1])
|
|
248
|
+
used = int(values[2])
|
|
249
|
+
free = int(values[3])
|
|
250
|
+
click.echo(
|
|
251
|
+
f"Total: {format_bytes(total)}\n"
|
|
252
|
+
f"Used: {format_bytes(used)}\n"
|
|
253
|
+
f"Free: {format_bytes(free)}\n"
|
|
254
|
+
f"Usage: {used/total*100:.1f}%"
|
|
255
|
+
)
|
|
256
|
+
elif system == "windows":
|
|
257
|
+
mem_info = run_command(
|
|
258
|
+
"wmic OS get TotalVisibleMemorySize,FreePhysicalMemory /Value"
|
|
259
|
+
)
|
|
260
|
+
if "TotalVisibleMemorySize" in mem_info and "FreePhysicalMemory" in mem_info:
|
|
261
|
+
try:
|
|
262
|
+
total = (
|
|
263
|
+
int(mem_info.split("TotalVisibleMemorySize=")[1].split("\n")[0])
|
|
264
|
+
* 1024
|
|
265
|
+
) # KB to bytes
|
|
266
|
+
free = (
|
|
267
|
+
int(mem_info.split("FreePhysicalMemory=")[1].split("\n")[0]) * 1024
|
|
268
|
+
) # KB to bytes
|
|
269
|
+
used = total - free
|
|
270
|
+
click.echo(
|
|
271
|
+
f"Total: {format_bytes(total)}\n"
|
|
272
|
+
f"Used: {format_bytes(used)}\n"
|
|
273
|
+
f"Free: {format_bytes(free)}\n"
|
|
274
|
+
f"Usage: {used/total*100:.1f}%"
|
|
275
|
+
)
|
|
276
|
+
except (IndexError, ValueError):
|
|
277
|
+
click.echo(mem_info)
|
|
278
|
+
else:
|
|
279
|
+
click.echo(mem_info)
|
|
280
|
+
|
|
281
|
+
# Disk Information
|
|
282
|
+
click.echo("\n" + "=" * 40 + " Disk Information " + "=" * 40)
|
|
283
|
+
if system == "windows":
|
|
284
|
+
disks = run_command("wmic logicaldisk get size,freespace,caption")
|
|
285
|
+
if "Caption" in disks:
|
|
286
|
+
lines = disks.split("\n")
|
|
287
|
+
result = [
|
|
288
|
+
"{:<5} {:<15} {:<15} {:<15} {:<10}".format(
|
|
289
|
+
"Drive", "Total Space", "Free Space", "Used Space", "% Used"
|
|
290
|
+
)
|
|
291
|
+
]
|
|
292
|
+
for line in lines[1:]:
|
|
293
|
+
parts = line.strip().split()
|
|
294
|
+
if len(parts) >= 3:
|
|
295
|
+
drive = parts[0]
|
|
296
|
+
try:
|
|
297
|
+
size = int(parts[1])
|
|
298
|
+
free = int(parts[2])
|
|
299
|
+
used = size - free
|
|
300
|
+
pct_used = (used / size) * 100 if size > 0 else 0
|
|
301
|
+
result.append(
|
|
302
|
+
"{:<5} {:<15} {:<15} {:<15} {:.1f}%".format(
|
|
303
|
+
drive,
|
|
304
|
+
format_bytes(size),
|
|
305
|
+
format_bytes(free),
|
|
306
|
+
format_bytes(used),
|
|
307
|
+
pct_used,
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
except (ValueError, IndexError):
|
|
311
|
+
continue
|
|
312
|
+
click.echo("\n".join(result))
|
|
313
|
+
else:
|
|
314
|
+
click.echo(disks)
|
|
315
|
+
else:
|
|
316
|
+
disks = run_command(
|
|
317
|
+
"df -h"
|
|
318
|
+
if system != "windows"
|
|
319
|
+
else "wmic logicaldisk get size,freespace,caption"
|
|
320
|
+
)
|
|
321
|
+
click.echo(parse_df_output(disks))
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
# Add aliases for the command
|
|
325
|
+
sysinfo = system_info
|
|
326
|
+
si = system_info
|
helper/commands/venv.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Virtual environment management commands for the helper CLI.
|
|
3
|
+
|
|
4
|
+
This module provides commands to manage Python virtual environments,
|
|
5
|
+
including activating and deactivating them from the command line.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def find_virtualenv(path=None):
|
|
15
|
+
"""Find virtual environment in the given path or current directory.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
path (str, optional): Path to search for virtual environment.
|
|
19
|
+
Defaults to current directory.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
str: Path to the activate script if found, None otherwise.
|
|
23
|
+
"""
|
|
24
|
+
if path is None:
|
|
25
|
+
path = os.getcwd()
|
|
26
|
+
|
|
27
|
+
# Check common virtual environment directories
|
|
28
|
+
venv_dirs = ["venv", ".venv"]
|
|
29
|
+
for venv_dir in venv_dirs:
|
|
30
|
+
activate_script = os.path.join(path, venv_dir, "bin", "activate")
|
|
31
|
+
if os.path.exists(activate_script):
|
|
32
|
+
return activate_script
|
|
33
|
+
|
|
34
|
+
# If not found in current directory, try parent directories
|
|
35
|
+
parent = os.path.dirname(path)
|
|
36
|
+
if parent != path: # Prevent infinite recursion
|
|
37
|
+
return find_virtualenv(parent)
|
|
38
|
+
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def source_virtualenv(venv_path=None):
|
|
43
|
+
"""Source a virtual environment.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
venv_path (str, optional): Path to the virtual environment.
|
|
47
|
+
If None, search in current and parent directories.
|
|
48
|
+
"""
|
|
49
|
+
if venv_path is None:
|
|
50
|
+
activate_script = find_virtualenv()
|
|
51
|
+
if not activate_script:
|
|
52
|
+
click.echo(
|
|
53
|
+
"Error: No virtual environment found in current or parent directories.",
|
|
54
|
+
err=True,
|
|
55
|
+
)
|
|
56
|
+
click.echo(
|
|
57
|
+
"Please specify the path to the virtual environment or "
|
|
58
|
+
"create one with 'python -m venv venv'"
|
|
59
|
+
)
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
else:
|
|
62
|
+
# If path is provided, check if it's a directory or activate script
|
|
63
|
+
if os.path.isdir(venv_path):
|
|
64
|
+
# If it's a directory, look for bin/activate
|
|
65
|
+
activate_script = os.path.join(venv_path, "bin", "activate")
|
|
66
|
+
if not os.path.exists(activate_script):
|
|
67
|
+
click.echo(f"Error: No activate script found in {venv_path}", err=True)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
elif os.path.isfile(venv_path):
|
|
70
|
+
# If it's a file, use it directly
|
|
71
|
+
activate_script = venv_path
|
|
72
|
+
else:
|
|
73
|
+
click.echo(f"Error: {venv_path} is not a valid file or directory", err=True)
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
|
|
76
|
+
# Get the absolute path to the activate script
|
|
77
|
+
activate_script = os.path.abspath(activate_script)
|
|
78
|
+
|
|
79
|
+
# Print the command to source the virtual environment
|
|
80
|
+
# The user needs to run this with 'eval $(h venv source [path])' or
|
|
81
|
+
# 'source <(h venv source [path])'
|
|
82
|
+
click.echo(f'source "{activate_script}"')
|
|
83
|
+
venv_dir = os.path.dirname(os.path.dirname(activate_script))
|
|
84
|
+
click.echo(f"# Virtual environment activated: {venv_dir}", err=True)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def deactivate_virtualenv():
|
|
88
|
+
"""Print the command to deactivate the current virtual environment."""
|
|
89
|
+
click.echo("deactivate")
|
|
90
|
+
click.echo("# Virtual environment deactivated", err=True)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@click.group()
|
|
94
|
+
def venv():
|
|
95
|
+
"""Manage Python virtual environments (v0.1.19)."""
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@venv.command()
|
|
99
|
+
@click.argument("path", required=False)
|
|
100
|
+
def source(path):
|
|
101
|
+
"""Source a Python virtual environment.
|
|
102
|
+
|
|
103
|
+
If no path is provided, searches for a virtual environment in the current or parent directories.
|
|
104
|
+
Looks for 'venv' first, then '.venv'.
|
|
105
|
+
|
|
106
|
+
Usage:
|
|
107
|
+
h venv source [PATH] # Source the specified virtual environment or auto-detect
|
|
108
|
+
eval $(h venv source) # In bash/zsh to activate the virtual environment
|
|
109
|
+
source <(h venv source) # Alternative syntax for bash/zsh
|
|
110
|
+
"""
|
|
111
|
+
source_virtualenv(path)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@venv.command()
|
|
115
|
+
def deactivate():
|
|
116
|
+
"""Deactivate the current virtual environment.
|
|
117
|
+
|
|
118
|
+
Usage:
|
|
119
|
+
h venv deactivate # Show the deactivate command
|
|
120
|
+
eval $(h venv deactivate) # In bash/zsh to deactivate the virtual environment
|
|
121
|
+
"""
|
|
122
|
+
deactivate_virtualenv()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# Add short aliases
|
|
126
|
+
s = source
|
|
127
|
+
d = deactivate
|
|
128
|
+
|
|
129
|
+
# Add the commands to the module
|
|
130
|
+
__all__ = ["venv", "source", "deactivate", "s", "d"]
|
helper/main.py
CHANGED
|
@@ -1,38 +1,51 @@
|
|
|
1
1
|
import click
|
|
2
2
|
import logging
|
|
3
3
|
import sys
|
|
4
|
-
|
|
4
|
+
import subprocess
|
|
5
|
+
from . import __version__
|
|
6
|
+
from .commands import (
|
|
7
|
+
internal_ip,
|
|
8
|
+
public_ip,
|
|
9
|
+
arch,
|
|
10
|
+
nixos,
|
|
11
|
+
docker,
|
|
12
|
+
speed,
|
|
13
|
+
system_info,
|
|
14
|
+
venv,
|
|
15
|
+
file,
|
|
16
|
+
)
|
|
17
|
+
|
|
5
18
|
|
|
6
19
|
class VerbosityCommand(click.Command):
|
|
7
20
|
def parse_args(self, ctx, args):
|
|
8
21
|
# Initialize verbosity from context if it exists
|
|
9
22
|
ctx.ensure_object(dict)
|
|
10
|
-
verbose = ctx.obj.get(
|
|
11
|
-
|
|
23
|
+
verbose = ctx.obj.get("verbosity", 0)
|
|
24
|
+
|
|
12
25
|
# Process args for verbosity flags
|
|
13
26
|
new_args = []
|
|
14
27
|
i = 0
|
|
15
28
|
while i < len(args):
|
|
16
29
|
arg = args[i]
|
|
17
|
-
if arg ==
|
|
30
|
+
if arg == "--verbose":
|
|
18
31
|
verbose += 1
|
|
19
|
-
elif arg.startswith(
|
|
20
|
-
verbose += arg.count(
|
|
32
|
+
elif arg.startswith("-v"):
|
|
33
|
+
verbose += arg.count("v")
|
|
21
34
|
else:
|
|
22
35
|
new_args.append(arg)
|
|
23
36
|
i += 1
|
|
24
|
-
|
|
37
|
+
|
|
25
38
|
# Update verbosity in context
|
|
26
|
-
ctx.obj[
|
|
27
|
-
|
|
39
|
+
ctx.obj["verbosity"] = verbose
|
|
40
|
+
|
|
28
41
|
# Set up logging
|
|
29
42
|
self._setup_logging(verbose)
|
|
30
|
-
|
|
43
|
+
|
|
31
44
|
# Continue with normal argument parsing
|
|
32
45
|
return super().parse_args(ctx, new_args)
|
|
33
|
-
|
|
46
|
+
|
|
34
47
|
def _setup_logging(self, verbose):
|
|
35
|
-
logger = logging.getLogger(
|
|
48
|
+
logger = logging.getLogger("docker-helper")
|
|
36
49
|
if verbose >= 3:
|
|
37
50
|
logger.setLevel(logging.DEBUG)
|
|
38
51
|
elif verbose == 2:
|
|
@@ -42,29 +55,30 @@ class VerbosityCommand(click.Command):
|
|
|
42
55
|
else:
|
|
43
56
|
logger.setLevel(logging.ERROR)
|
|
44
57
|
|
|
58
|
+
|
|
45
59
|
class VerbosityGroup(click.Group):
|
|
46
60
|
def make_context(self, info_name, args, parent=None, **extra):
|
|
47
61
|
# Pre-process args to find verbosity flags
|
|
48
62
|
verbose = 0
|
|
49
63
|
processed_args = []
|
|
50
|
-
|
|
64
|
+
|
|
51
65
|
for arg in args:
|
|
52
|
-
if arg ==
|
|
66
|
+
if arg == "--verbose":
|
|
53
67
|
verbose += 1
|
|
54
|
-
elif arg.startswith(
|
|
55
|
-
verbose += arg.count(
|
|
68
|
+
elif arg.startswith("-v"):
|
|
69
|
+
verbose += arg.count("v")
|
|
56
70
|
else:
|
|
57
71
|
processed_args.append(arg)
|
|
58
|
-
|
|
72
|
+
|
|
59
73
|
# Create context with processed args
|
|
60
74
|
ctx = super().make_context(info_name, processed_args, parent=parent, **extra)
|
|
61
|
-
|
|
75
|
+
|
|
62
76
|
# Set verbosity in context
|
|
63
77
|
ctx.ensure_object(dict)
|
|
64
|
-
ctx.obj[
|
|
65
|
-
|
|
78
|
+
ctx.obj["verbosity"] = verbose
|
|
79
|
+
|
|
66
80
|
# Set up logging
|
|
67
|
-
logger = logging.getLogger(
|
|
81
|
+
logger = logging.getLogger("docker-helper")
|
|
68
82
|
if verbose >= 3:
|
|
69
83
|
logger.setLevel(logging.DEBUG)
|
|
70
84
|
elif verbose == 2:
|
|
@@ -73,24 +87,48 @@ class VerbosityGroup(click.Group):
|
|
|
73
87
|
logger.setLevel(logging.WARNING)
|
|
74
88
|
else:
|
|
75
89
|
logger.setLevel(logging.ERROR)
|
|
76
|
-
|
|
90
|
+
|
|
77
91
|
return ctx
|
|
78
92
|
|
|
79
|
-
|
|
93
|
+
|
|
94
|
+
@click.group(
|
|
95
|
+
cls=VerbosityGroup,
|
|
96
|
+
context_settings={
|
|
97
|
+
"help_option_names": ["-h", "--help"],
|
|
98
|
+
"token_normalize_func": lambda x: "helper" if x == "h" else x,
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
@click.version_option(__version__, "-V", "--version", message="%(prog)s version %(version)s")
|
|
80
102
|
def cli():
|
|
81
|
-
"""Helper CLI - quick system info
|
|
103
|
+
"""Helper CLI - quick system info (v{})
|
|
104
|
+
|
|
105
|
+
You can use 'h' as a shortcut for 'helper' command.
|
|
106
|
+
Example: h docker ps
|
|
107
|
+
|
|
108
|
+
For detailed help on a specific command, use: helper <command> --help
|
|
109
|
+
""".format(__version__)
|
|
82
110
|
# Set up basic logging
|
|
83
111
|
logging.basicConfig(
|
|
84
|
-
format=
|
|
85
|
-
level=logging.ERROR
|
|
112
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
113
|
+
level=logging.ERROR,
|
|
86
114
|
)
|
|
87
115
|
|
|
116
|
+
|
|
88
117
|
# Register all commands
|
|
89
118
|
cli.add_command(internal_ip.internal_ip)
|
|
90
119
|
cli.add_command(public_ip.public_ip)
|
|
91
120
|
cli.add_command(arch.arch)
|
|
92
121
|
cli.add_command(nixos.nixos, name="nixos")
|
|
93
122
|
cli.add_command(docker.docker, name="docker")
|
|
123
|
+
cli.add_command(speed.speed, name="speed")
|
|
124
|
+
cli.add_command(speed.speed, name="sp")
|
|
125
|
+
cli.add_command(system_info.system_info, name="system-info")
|
|
126
|
+
cli.add_command(system_info.system_info, name="sysinfo")
|
|
127
|
+
cli.add_command(system_info.system_info, name="si")
|
|
128
|
+
cli.add_command(venv.venv, name="v")
|
|
129
|
+
cli.add_command(file.file(), name="file")
|
|
130
|
+
cli.add_command(file.file(), name="f")
|
|
131
|
+
|
|
94
132
|
|
|
95
133
|
@cli.command()
|
|
96
134
|
@click.pass_context
|
|
@@ -103,7 +141,10 @@ def all(ctx):
|
|
|
103
141
|
click.echo("\n=== Architecture ===")
|
|
104
142
|
ctx.invoke(arch.arch)
|
|
105
143
|
click.echo("\n=== NixOS ===")
|
|
106
|
-
ctx.invoke(nixos.nixos,
|
|
144
|
+
ctx.invoke(nixos.nixos, "version")
|
|
145
|
+
click.echo("\n=== System Info ===")
|
|
146
|
+
ctx.invoke(system_info.system_info)
|
|
147
|
+
|
|
107
148
|
|
|
108
149
|
if __name__ == "__main__":
|
|
109
150
|
cli()
|
helper/table.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import json
|
|
3
|
+
import yaml
|
|
4
|
+
from tabulate import tabulate
|
|
5
|
+
|
|
6
|
+
data = [{"name": "Huy", "age": 23}, {"name": "An", "age": 25}]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command()
|
|
10
|
+
@click.option(
|
|
11
|
+
"--format",
|
|
12
|
+
"-f",
|
|
13
|
+
type=click.Choice(["json", "yaml", "table", "text"]),
|
|
14
|
+
default="table",
|
|
15
|
+
)
|
|
16
|
+
def show(format):
|
|
17
|
+
if format == "json":
|
|
18
|
+
click.echo(json.dumps(data, indent=2))
|
|
19
|
+
elif format == "yaml":
|
|
20
|
+
click.echo(yaml.dump(data))
|
|
21
|
+
elif format == "table":
|
|
22
|
+
click.echo(tabulate(data, headers="keys"))
|
|
23
|
+
else:
|
|
24
|
+
for item in data:
|
|
25
|
+
click.echo(f"{item['name']} - {item['age']}")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
show()
|