Add helloworld utility and update welcome message in .bashrc
This commit is contained in:
4
.bashrc
4
.bashrc
@@ -181,6 +181,6 @@ if [ -f $HOME/.bashrc.local ]; then
|
||||
fi
|
||||
|
||||
# Display a welcome message for interactive shells
|
||||
if [ -t 1 ]; then
|
||||
dotf hello
|
||||
if [ -t 1 ] && command -v helloworld &> /dev/null; then
|
||||
helloworld
|
||||
fi
|
||||
|
365
config/ansible/tasks/global/utils/helloworld.go
Normal file
365
config/ansible/tasks/global/utils/helloworld.go
Normal file
@@ -0,0 +1,365 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ANSI color codes
|
||||
var colors = map[string]string{
|
||||
"black": "\033[0;30m",
|
||||
"red": "\033[0;31m",
|
||||
"green": "\033[0;32m",
|
||||
"yellow": "\033[0;33m",
|
||||
"blue": "\033[0;34m",
|
||||
"purple": "\033[0;35m",
|
||||
"cyan": "\033[0;36m",
|
||||
"white": "\033[0;37m",
|
||||
"grey": "\033[0;90m",
|
||||
"reset": "\033[0m",
|
||||
}
|
||||
|
||||
// DistroIcon represents a distro icon and color
|
||||
type DistroIcon struct {
|
||||
Icon string
|
||||
Color string
|
||||
}
|
||||
|
||||
// DotfilesStatus represents the git status of dotfiles
|
||||
type DotfilesStatus struct {
|
||||
IsDirty bool
|
||||
Untracked int
|
||||
Modified int
|
||||
Staged int
|
||||
CommitHash string
|
||||
Unpushed int
|
||||
}
|
||||
|
||||
func main() {
|
||||
welcome()
|
||||
}
|
||||
|
||||
func rainbowColor(text string, freq float64, offset float64) string {
|
||||
var result strings.Builder
|
||||
for i, char := range text {
|
||||
if strings.TrimSpace(string(char)) != "" { // Only color non-whitespace characters
|
||||
// Calculate RGB values using sine waves with phase shifts
|
||||
r := int(127*math.Sin(freq*float64(i)+offset+0) + 128)
|
||||
g := int(127*math.Sin(freq*float64(i)+offset+2*math.Pi/3) + 128)
|
||||
b := int(127*math.Sin(freq*float64(i)+offset+4*math.Pi/3) + 128)
|
||||
|
||||
// Apply the RGB color to the character
|
||||
result.WriteString(fmt.Sprintf("\033[38;2;%d;%d;%dm%c\033[0m", r, g, b, char))
|
||||
} else {
|
||||
result.WriteRune(char)
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
func printLogo() {
|
||||
logo := ` __ ___ _ ____ __ _____ __
|
||||
/ |/ /__ ____ ____ ____ ( )_____ / __ \____ / /_/ __(_) /__ _____
|
||||
/ /|_/ / _ \/ __ \/ __ \/ __ \|// ___/ / / / / __ \/ __/ /_/ / / _ \/ ___/
|
||||
/ / / / __/ / / / / / / /_/ / (__ ) / /_/ / /_/ / /_/ __/ / / __(__ )
|
||||
/_/ /_/\___/_/ /_/_/ /_/\____/ /____/ /_____/\____/\__/_/ /_/_/\___/____/`
|
||||
|
||||
lines := strings.Split(logo, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.TrimSpace(line) != "" {
|
||||
fmt.Println(rainbowColor(line, 0.1, 0))
|
||||
} else {
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func getLastSSHLogin() string {
|
||||
user := os.Getenv("USER")
|
||||
if user == "" {
|
||||
user = os.Getenv("USERNAME")
|
||||
}
|
||||
if user == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Try lastlog first
|
||||
cmd := exec.Command("lastlog", "-u", user)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
// Try lastlog2
|
||||
cmd = exec.Command("lastlog2", user)
|
||||
output, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
if len(lines) >= 2 {
|
||||
parts := strings.Fields(lines[1])
|
||||
if len(parts) >= 7 && strings.Contains(parts[1], "ssh") {
|
||||
ip := parts[2]
|
||||
timeStr := strings.Join(parts[3:], " ")
|
||||
return fmt.Sprintf("%sLast SSH login%s%s %s%s from%s %s",
|
||||
colors["cyan"], colors["reset"], colors["yellow"], timeStr, colors["cyan"], colors["yellow"], ip)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func checkDotfilesStatus() *DotfilesStatus {
|
||||
dotfilesPath := os.Getenv("DOTFILES_PATH")
|
||||
if dotfilesPath == "" {
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
dotfilesPath = filepath.Join(homeDir, ".dotfiles")
|
||||
}
|
||||
|
||||
gitPath := filepath.Join(dotfilesPath, ".git")
|
||||
if _, err := os.Stat(gitPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
status := &DotfilesStatus{}
|
||||
|
||||
// Check git status
|
||||
cmd := exec.Command("git", "status", "--porcelain")
|
||||
cmd.Dir = dotfilesPath
|
||||
output, err := cmd.Output()
|
||||
if err == nil && strings.TrimSpace(string(output)) != "" {
|
||||
status.IsDirty = true
|
||||
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "??") {
|
||||
status.Untracked++
|
||||
}
|
||||
if strings.HasPrefix(line, " M") || strings.HasPrefix(line, "MM") {
|
||||
status.Modified++
|
||||
}
|
||||
if strings.HasPrefix(line, "M ") || strings.HasPrefix(line, "A ") {
|
||||
status.Staged++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get commit hash
|
||||
cmd = exec.Command("git", "rev-parse", "--short", "HEAD")
|
||||
cmd.Dir = dotfilesPath
|
||||
output, err = cmd.Output()
|
||||
if err == nil {
|
||||
status.CommitHash = strings.TrimSpace(string(output))
|
||||
}
|
||||
|
||||
// Count unpushed commits
|
||||
cmd = exec.Command("git", "log", "--oneline", "@{u}..")
|
||||
cmd.Dir = dotfilesPath
|
||||
output, err = cmd.Output()
|
||||
if err == nil {
|
||||
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||
if len(lines) > 0 && lines[0] != "" {
|
||||
status.Unpushed = len(lines)
|
||||
}
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
func getCondensedStatus() (string, string) {
|
||||
var statusParts []string
|
||||
var hashInfo string
|
||||
|
||||
// Check trash status
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
trashPath := filepath.Join(homeDir, ".local", "share", "Trash", "files")
|
||||
if entries, err := os.ReadDir(trashPath); err == nil {
|
||||
count := len(entries)
|
||||
if count > 0 {
|
||||
statusParts = append(statusParts, fmt.Sprintf("[!] %d file(s) in trash", count))
|
||||
}
|
||||
}
|
||||
|
||||
// Check dotfiles status
|
||||
dotfilesStatus := checkDotfilesStatus()
|
||||
if dotfilesStatus != nil {
|
||||
if dotfilesStatus.IsDirty {
|
||||
statusParts = append(statusParts, fmt.Sprintf("%sdotfiles is dirty%s", colors["yellow"], colors["reset"]))
|
||||
statusParts = append(statusParts, fmt.Sprintf("%s[%d] untracked%s", colors["red"], dotfilesStatus.Untracked, colors["reset"]))
|
||||
statusParts = append(statusParts, fmt.Sprintf("%s[%d] modified%s", colors["yellow"], dotfilesStatus.Modified, colors["reset"]))
|
||||
statusParts = append(statusParts, fmt.Sprintf("%s[%d] staged%s", colors["green"], dotfilesStatus.Staged, colors["reset"]))
|
||||
}
|
||||
|
||||
if dotfilesStatus.CommitHash != "" {
|
||||
hashInfo = fmt.Sprintf("%s[%s%s%s]%s", colors["white"], colors["blue"], dotfilesStatus.CommitHash, colors["white"], colors["reset"])
|
||||
if dotfilesStatus.IsDirty {
|
||||
statusParts = append(statusParts, hashInfo)
|
||||
hashInfo = ""
|
||||
}
|
||||
}
|
||||
|
||||
if dotfilesStatus.Unpushed > 0 {
|
||||
statusParts = append(statusParts, fmt.Sprintf("%s[!] You have %d commit(s) to push%s", colors["yellow"], dotfilesStatus.Unpushed, colors["reset"]))
|
||||
}
|
||||
} else {
|
||||
statusParts = append(statusParts, "Unable to check dotfiles status")
|
||||
}
|
||||
|
||||
statusLine := ""
|
||||
if len(statusParts) > 0 {
|
||||
statusLine = strings.Join(statusParts, " - ")
|
||||
}
|
||||
return statusLine, hashInfo
|
||||
}
|
||||
|
||||
func runDotfilesCommand(args ...string) (string, error) {
|
||||
cmd := exec.Command("dotfiles", args...)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
func getDistroIcon() (string, string) {
|
||||
distroIcons := map[string]DistroIcon{
|
||||
"windows": {"\uf17a", colors["blue"]}, // blue
|
||||
"linux": {"\uf17c", colors["yellow"]}, // yellow
|
||||
"ubuntu": {"\uf31b", "\033[38;5;208m"}, // orange (ANSI 208)
|
||||
"debian": {"\uf306", colors["red"]}, // red
|
||||
"arch": {"\uf303", colors["cyan"]}, // cyan
|
||||
"fedora": {"\uf30a", colors["blue"]}, // blue
|
||||
"alpine": {"\uf300", colors["cyan"]}, // cyan
|
||||
"macos": {"\uf179", colors["white"]}, // white
|
||||
"darwin": {"\uf179", colors["white"]}, // white
|
||||
"osx": {"\uf179", colors["white"]}, // white
|
||||
}
|
||||
|
||||
distro, err := runDotfilesCommand("variables", "get", "Platform.Distro", "--format", "raw")
|
||||
if err != nil {
|
||||
distro = strings.ToLower(runtime.GOOS)
|
||||
} else {
|
||||
distro = strings.ToLower(distro)
|
||||
}
|
||||
|
||||
if icon, exists := distroIcons[distro]; exists {
|
||||
return icon.Icon, icon.Color
|
||||
}
|
||||
|
||||
// Try partial match
|
||||
for key, icon := range distroIcons {
|
||||
if strings.Contains(distro, key) {
|
||||
return icon.Icon, icon.Color
|
||||
}
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func detectShell() string {
|
||||
// Check for PowerShell profile
|
||||
if os.Getenv("PROFILE") != "" || os.Getenv("PW_SH_PROFILE") != "" || os.Getenv("PSModulePath") != "" {
|
||||
return "powershell"
|
||||
}
|
||||
|
||||
if shell := os.Getenv("SHELL"); shell != "" {
|
||||
return filepath.Base(shell)
|
||||
}
|
||||
|
||||
if comspec := os.Getenv("COMSPEC"); comspec != "" {
|
||||
if strings.HasSuffix(strings.ToLower(comspec), "cmd.exe") {
|
||||
if os.Getenv("PROFILE") != "" {
|
||||
return "Powershell"
|
||||
}
|
||||
return "CMD"
|
||||
}
|
||||
return filepath.Base(comspec)
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func welcome() {
|
||||
printLogo()
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
hostname = "unknown-host"
|
||||
}
|
||||
|
||||
// Get distro icon
|
||||
distroIcon, iconColor := getDistroIcon()
|
||||
|
||||
// Get username
|
||||
username := os.Getenv("USER")
|
||||
if username == "" {
|
||||
username = os.Getenv("USERNAME")
|
||||
}
|
||||
if username == "" {
|
||||
username = "user"
|
||||
}
|
||||
|
||||
// Get SSH login info
|
||||
sshLogin := getLastSSHLogin()
|
||||
|
||||
// Get shell and arch
|
||||
shell := detectShell()
|
||||
arch := runtime.GOARCH
|
||||
|
||||
// Capitalize shell and arch for display
|
||||
shellDisp := strings.Title(shell)
|
||||
archDisp := strings.ToUpper(arch)
|
||||
|
||||
// Get package managers
|
||||
pkgMgrs, err := runDotfilesCommand("variables", "get", "Platform.AvailablePackageManagers", "--format", "raw")
|
||||
if err != nil {
|
||||
pkgMgrs = ""
|
||||
}
|
||||
|
||||
// Compact single line: user@hostname with icon, shell, arch
|
||||
fmt.Printf("%s%s%s@%s%s", colors["green"], username, colors["cyan"], colors["yellow"], hostname)
|
||||
if distroIcon != "" {
|
||||
fmt.Printf(" %s%s", iconColor, distroIcon)
|
||||
}
|
||||
fmt.Printf("%s running %s%s%s/%s%s", colors["cyan"], colors["blue"], shellDisp, colors["cyan"], colors["purple"], archDisp)
|
||||
|
||||
if pkgMgrs != "" {
|
||||
// Parse and color package managers
|
||||
pkgMgrs = strings.Trim(pkgMgrs, "[]")
|
||||
pmList := strings.Fields(strings.ReplaceAll(pkgMgrs, ",", ""))
|
||||
pmColors := []string{colors["yellow"], colors["green"], colors["cyan"], colors["red"], colors["blue"]}
|
||||
var coloredPMs []string
|
||||
|
||||
for i, pm := range pmList {
|
||||
color := pmColors[i%len(pmColors)]
|
||||
coloredPMs = append(coloredPMs, fmt.Sprintf("%s%s", color, pm))
|
||||
}
|
||||
|
||||
fmt.Printf("%s [%s%s]", colors["cyan"], strings.Join(coloredPMs, colors["cyan"]+"/"), colors["reset"])
|
||||
} else {
|
||||
fmt.Printf("%s", colors["reset"])
|
||||
}
|
||||
|
||||
// Get status info
|
||||
condensedStatus, hashInfo := getCondensedStatus()
|
||||
|
||||
// Add hash to same line if dotfiles is clean
|
||||
if hashInfo != "" {
|
||||
fmt.Printf(" %s", hashInfo)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Display last SSH login info if available
|
||||
if sshLogin != "" {
|
||||
fmt.Printf("%s%s\n", sshLogin, colors["reset"])
|
||||
}
|
||||
|
||||
// Display condensed status line only if there are issues
|
||||
if condensedStatus != "" {
|
||||
fmt.Printf("%s%s%s\n", colors["yellow"], condensedStatus, colors["reset"])
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user