Skip to content

Commit 0159eda

Browse files
Create main.py
1 parent 3d621f0 commit 0159eda

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

main.py

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
import os
2+
import sys
3+
import subprocess
4+
import threading
5+
import queue
6+
import time
7+
import logging
8+
from rich.console import Console
9+
from rich.panel import Panel
10+
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
11+
from rich.text import Text
12+
from rich.style import Style
13+
from rich.live import Live
14+
from rich import box
15+
16+
# Setup logging
17+
logging.basicConfig(
18+
filename='/tmp/hacker-update.log',
19+
level=logging.DEBUG,
20+
format='%(asctime)s - %(levelname)s - %(message)s',
21+
datefmt='%Y-%m-%d %H:%M:%S'
22+
)
23+
24+
console = Console()
25+
26+
# Constants for styling
27+
HEADER_WIDTH = 80
28+
COLOR_MAP = {
29+
"APT System Update": "bright_magenta",
30+
"Flatpak Update": "bright_yellow",
31+
"Snap Update": "bright_blue",
32+
"Firmware Update": "bright_green",
33+
}
34+
35+
# Structure for commands
36+
commands = [
37+
{
38+
"name": "APT System Update",
39+
"commands": [
40+
{"name": "APT Update", "cmd": "sudo apt update", "color": "bright_magenta", "list_cmd": None},
41+
{"name": "APT Upgrade", "cmd": "sudo apt upgrade -y", "color": "bright_magenta", "list_cmd": "apt list --upgradable"},
42+
{"name": "APT Autoremove", "cmd": "sudo apt autoremove -y", "color": "bright_magenta", "list_cmd": None},
43+
{"name": "APT Autoclean", "cmd": "sudo apt autoclean", "color": "bright_magenta", "list_cmd": None},
44+
]
45+
},
46+
{
47+
"name": "Flatpak Update",
48+
"commands": [
49+
{"name": "Flatpak Update", "cmd": "flatpak update -y", "color": "bright_yellow", "list_cmd": "flatpak remote-ls --updates flathub"},
50+
]
51+
},
52+
{
53+
"name": "Snap Update",
54+
"commands": [
55+
{"name": "Snap Refresh", "cmd": "sudo snap refresh", "color": "bright_blue", "list_cmd": "snap refresh --list"},
56+
]
57+
},
58+
{
59+
"name": "Firmware Update",
60+
"commands": [
61+
{"name": "Firmware Refresh", "cmd": "sudo fwupdmgr refresh", "color": "bright_green", "list_cmd": None},
62+
{"name": "Firmware Update", "cmd": "sudo fwupdmgr update", "color": "bright_green", "list_cmd": "fwupdmgr get-updates"},
63+
]
64+
},
65+
]
66+
67+
def print_header():
68+
title = Text("HackerOS Update Utility v0.0.1", style="bold bright_cyan")
69+
panel = Panel(title, width=HEADER_WIDTH, box=box.HEAVY, expand=True, style="on black")
70+
console.print(panel)
71+
console.print(Text("Initializing updates...", style="italic bright_blue", justify="center"))
72+
console.print()
73+
74+
def print_section_header(name):
75+
color = COLOR_MAP.get(name, "bright_white")
76+
header_text = Text(name, style=f"bold white on {color}")
77+
panel = Panel(header_text, width=HEADER_WIDTH, box=box.DOUBLE_EDGE, expand=True)
78+
console.print(panel)
79+
console.print()
80+
81+
def run_command(cmd, color, is_list=False):
82+
process = subprocess.Popen(
83+
["sh", "-c", cmd],
84+
stdout=subprocess.PIPE,
85+
stderr=subprocess.PIPE,
86+
text=True,
87+
bufsize=1
88+
)
89+
stdout_queue = queue.Queue()
90+
stderr_queue = queue.Queue()
91+
92+
def read_stdout():
93+
for line in iter(process.stdout.readline, ''):
94+
stdout_queue.put(line.strip())
95+
process.stdout.close()
96+
97+
def read_stderr():
98+
for line in iter(process.stderr.readline, ''):
99+
stderr_queue.put(line.strip())
100+
process.stderr.close()
101+
102+
stdout_thread = threading.Thread(target=read_stdout)
103+
stderr_thread = threading.Thread(target=read_stderr)
104+
stdout_thread.start()
105+
stderr_thread.start()
106+
107+
stdout_lines = []
108+
stderr_lines = []
109+
110+
while process.poll() is None or not stdout_queue.empty() or not stderr_queue.empty():
111+
try:
112+
line = stdout_queue.get_nowait()
113+
if not is_list:
114+
console.print(Text(line, style=color))
115+
stdout_lines.append(line)
116+
logging.info(f"STDOUT: {line}")
117+
except queue.Empty:
118+
pass
119+
try:
120+
line = stderr_queue.get_nowait()
121+
if not is_list:
122+
console.print(Text(line, style="bright_red"))
123+
stderr_lines.append(line)
124+
logging.error(f"STDERR: {line}")
125+
except queue.Empty:
126+
pass
127+
time.sleep(0.01)
128+
129+
stdout_thread.join()
130+
stderr_thread.join()
131+
return_code = process.wait()
132+
133+
return '\n'.join(stdout_lines), '\n'.join(stderr_lines), return_code == 0
134+
135+
def list_updates(list_cmd, name, color):
136+
with console.status(f"[green]Listing updates for {name}...[/]", spinner="dots"):
137+
stdout, stderr, success = run_command(list_cmd, color, is_list=True)
138+
if success and stdout.strip():
139+
console.print(Text(f"Updates available for {name}:", style=f"bold {color}"))
140+
lines = [line for line in stdout.splitlines() if line.strip() and "Listing..." not in line and "All snaps up to date." not in line]
141+
if lines:
142+
for i, line in enumerate(lines, 1):
143+
console.print(Text(f" {i:2}. {line}", style="bright_white"))
144+
else:
145+
console.print(Text(" None", style="bright_white"))
146+
elif stderr:
147+
console.print(Text(f"Error listing updates: {stderr}", style="bright_red"))
148+
else:
149+
console.print(Text(f"No updates available for {name}.", style="bright_green"))
150+
console.print()
151+
152+
def print_menu():
153+
menu_text = Text.assemble(
154+
Text("Update Process Completed\n", style="bold bright_green", justify="center"),
155+
Text("Choose an action:\n", style="bold white", justify="center"),
156+
Text("(E)xit (S)hutdown (R)eboot\n", style="bright_yellow", justify="center"),
157+
Text("(L)og Out (T)ry Again (H) Show Logs", style="bright_yellow", justify="center")
158+
)
159+
panel = Panel(menu_text, width=HEADER_WIDTH, box=box.ROUNDED, expand=True, style="bright_cyan")
160+
console.print(panel)
161+
console.print(Text("Select an option:", style="italic white", justify="center"))
162+
163+
def print_logs(logs):
164+
log_content = Text()
165+
for name, log, is_stdout in logs:
166+
if log.strip():
167+
log_type = "Output" if is_stdout else "Error"
168+
log_color = "bright_white" if is_stdout else "bright_red"
169+
log_content.append(f"{log_type} for {name}:\n", style=f"bold {log_color}")
170+
for line in log.splitlines():
171+
log_content.append(f" {line}\n", style=log_color)
172+
log_content.append("\n")
173+
panel = Panel(log_content, title="Update Logs", width=HEADER_WIDTH, box=box.SQUARE, style="on bright_cyan", expand=True)
174+
console.print(panel)
175+
console.print()
176+
177+
def print_action(message, color):
178+
action_text = Text(message, style=f"white on {color}")
179+
panel = Panel(action_text, width=HEADER_WIDTH // 2, box=box.MINIMAL, expand=False, style="bold")
180+
console.print(panel, justify="center")
181+
time.sleep(0.2)
182+
183+
def get_single_key():
184+
import termios, tty
185+
fd = sys.stdin.fileno()
186+
old_attr = termios.tcgetattr(fd)
187+
try:
188+
tty.setraw(fd)
189+
return sys.stdin.read(1).lower()
190+
finally:
191+
termios.tcsetattr(fd, termios.TCDRAIN, old_attr)
192+
193+
def main():
194+
print_header()
195+
logs = []
196+
197+
for section in commands:
198+
print_section_header(section["name"])
199+
total_steps = len(section["commands"])
200+
progress = Progress(
201+
SpinnerColumn(),
202+
BarColumn(bar_width=None),
203+
TextColumn("[progress.description]{task.description}"),
204+
TextColumn("{task.completed}/{task.total}"),
205+
"|",
206+
TextColumn("{task.fields[msg]}"),
207+
"| ETA:",
208+
TimeRemainingColumn(),
209+
console=console,
210+
expand=True
211+
)
212+
task_id = progress.add_task(f"[bright_cyan]{section['name']:<30}[/]", total=total_steps, msg="")
213+
214+
with Live(progress, refresh_per_second=20):
215+
for cmd_info in section["commands"]:
216+
progress.update(task_id, description=f"[bright_cyan]{section['name']:<30}[/]", msg=f"{cmd_info['name']}")
217+
if cmd_info["list_cmd"]:
218+
list_updates(cmd_info["list_cmd"], cmd_info["name"], cmd_info["color"])
219+
220+
with console.status(f"[green]Executing: {cmd_info['name']}[/]", spinner="dots"):
221+
stdout, stderr, success = run_command(cmd_info["cmd"], cmd_info["color"])
222+
223+
logs.append((cmd_info["name"], stdout, True))
224+
if stderr:
225+
logs.append((cmd_info["name"], stderr, False))
226+
227+
status_msg = Text(cmd_info["name"] + ": " + ("Completed" if success else "Failed"), style="bright_green bold" if success else "bright_red bold")
228+
console.print(status_msg)
229+
console.print()
230+
231+
progress.advance(task_id)
232+
233+
progress.update(task_id, description=f"[bright_cyan]{section['name']:<30}[/]", msg=f"{section['name']} completed", completed=total_steps)
234+
console.print(Text(f"{section['name']} completed", style="bold bright_green"))
235+
console.print()
236+
time.sleep(0.3)
237+
238+
while True:
239+
print_menu()
240+
choice = get_single_key()
241+
if choice == 'e':
242+
print_action("Exiting Update Utility", "bright_blue")
243+
break
244+
elif choice == 's':
245+
print_action("Shutting Down System", "bright_blue")
246+
subprocess.run(["sudo", "poweroff"])
247+
break
248+
elif choice == 'r':
249+
print_action("Rebooting System", "bright_blue")
250+
subprocess.run(["sudo", "reboot"])
251+
break
252+
elif choice == 'l':
253+
print_action("Logging Out", "bright_blue")
254+
username = os.getlogin()
255+
subprocess.run(["pkill", "-u", username])
256+
break
257+
elif choice == 't':
258+
print_action("Restarting Update Process", "bright_blue")
259+
main()
260+
return
261+
elif choice == 'h':
262+
print_logs(logs)
263+
else:
264+
print_action("Invalid Option", "bright_red")
265+
266+
if __name__ == "__main__":
267+
main()

0 commit comments

Comments
 (0)