dotfiles/bin/actions/hello.py
2025-03-10 20:33:01 +01:00

164 lines
6.2 KiB
Python
Executable File

#!/usr/bin/env python3
import os
import sys
import subprocess
from datetime import datetime
# Import helper functions
sys.path.append(os.path.join(os.path.expanduser("~/.dotfiles"), "bin"))
from helpers.functions import printfe, logo, _rainbow_color, COLORS
def get_last_ssh_login():
"""Get information about the last SSH login"""
try:
result = subprocess.run(['lastlog', '-u', os.environ.get("USER", "")],
capture_output=True, text=True)
# If lastlog didn't work try lastlog2
if result.returncode != 0:
result = subprocess.run(['lastlog2', os.environ.get("USER", "")],
capture_output=True, text=True)
if result.returncode == 0:
lines = result.stdout.strip().split('\n')
if len(lines) >= 2: # Header line + data line
# Parse the last login line - example format:
# menno ssh 100.99.23.98 Mon Mar 10 19:09:43 +0100 2025
parts = lines[1].split()
if len(parts) >= 7 and 'ssh' in parts[1]: # Check if it's an SSH login
# Extract IP address from the third column
ip = parts[2]
# Time is the rest of the line starting from position 3
time_str = ' '.join(parts[3:])
return f"{COLORS['cyan']}Last SSH login{COLORS['reset']}{COLORS['yellow']} {time_str}{COLORS['cyan']} from{COLORS['yellow']} {ip}"
return None
except Exception as e:
# For debugging, you might want to print the exception
# print(f"Error getting SSH login: {str(e)}")
return None
def check_dotfiles_status():
"""Check if the dotfiles repository is dirty"""
dotfiles_path = os.environ.get("DOTFILES_PATH", os.path.expanduser("~/.dotfiles"))
try:
if not os.path.isdir(os.path.join(dotfiles_path, ".git")):
return None
# Check for git status details
status = {
'is_dirty': False,
'untracked': 0,
'modified': 0,
'staged': 0,
'commit_hash': '',
'unpushed': 0
}
# Get status of files
result = subprocess.run(['git', 'status', '--porcelain'],
cwd=dotfiles_path,
capture_output=True, text=True)
if result.stdout.strip():
status['is_dirty'] = True
for line in result.stdout.splitlines():
if line.startswith('??'):
status['untracked'] += 1
if line.startswith(' M') or line.startswith('MM'):
status['modified'] += 1
if line.startswith('M ') or line.startswith('A '):
status['staged'] += 1
# Get current commit hash
result = subprocess.run(['git', 'rev-parse', '--short', 'HEAD'],
cwd=dotfiles_path,
capture_output=True, text=True)
if result.returncode == 0:
status['commit_hash'] = result.stdout.strip()
# Count unpushed commits
# Fix: Remove capture_output and set stdout explicitly
result = subprocess.run(['git', 'log', '--oneline', '@{u}..'],
cwd=dotfiles_path,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True)
if result.returncode == 0:
status['unpushed'] = len(result.stdout.splitlines())
return status
except Exception as e:
print(f"Error checking dotfiles status: {str(e)}")
return None
def get_condensed_status():
"""Generate a condensed status line for trash and git status"""
status_parts = []
# Check trash status
trash_path = os.path.expanduser("~/.local/share/Trash/files")
try:
if os.path.exists(trash_path):
items = os.listdir(trash_path)
count = len(items)
if count > 0:
status_parts.append(f"[!] {count} file(s) in trash")
except Exception:
pass
# Check dotfiles status
dotfiles_status = check_dotfiles_status()
if dotfiles_status is not None:
if dotfiles_status['is_dirty']:
status_parts.append(f"{COLORS['yellow']}dotfiles is dirty{COLORS['reset']}")
status_parts.append(f"{COLORS['red']}[{dotfiles_status['untracked']}] untracked{COLORS['reset']}")
status_parts.append(f"{COLORS['yellow']}[{dotfiles_status['modified']}] modified{COLORS['reset']}")
status_parts.append(f"{COLORS['green']}[{dotfiles_status['staged']}] staged{COLORS['reset']}")
if dotfiles_status['commit_hash']:
status_parts.append(f"{COLORS['white']}[{COLORS['blue']}{dotfiles_status['commit_hash']}{COLORS['white']}]{COLORS['reset']}")
if dotfiles_status['unpushed'] > 0:
status_parts.append(f"{COLORS['yellow']}[!] You have {dotfiles_status['unpushed']} commit(s) to push{COLORS['reset']}")
else:
status_parts.append("Unable to check dotfiles status")
if status_parts:
return " - ".join(status_parts)
return None
def welcome():
"""Display welcome message with hostname and username"""
print()
# Get hostname and username
hostname = os.uname().nodename
username = os.environ.get("USER", os.environ.get("USERNAME", "user"))
# Get SSH login info first
ssh_login = get_last_ssh_login()
print(f"{COLORS['cyan']}You're logged in on [", end="")
print(_rainbow_color(hostname), end="")
print(f"{COLORS['cyan']}] as [", end="")
print(_rainbow_color(username), end="")
print(f"{COLORS['cyan']}]{COLORS['reset']}")
# Display last SSH login info if available
if ssh_login:
print(f"{ssh_login}{COLORS['reset']}")
# Display condensed status line
condensed_status = get_condensed_status()
if condensed_status:
print(f"{COLORS['yellow']}{condensed_status}{COLORS['reset']}")
def main():
logo(continue_after=True)
welcome()
return 0
if __name__ == "__main__":
sys.exit(main())