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
|
fi
|
||||||
|
|
||||||
# Display a welcome message for interactive shells
|
# Display a welcome message for interactive shells
|
||||||
if [ -t 1 ]; then
|
if [ -t 1 ] && command -v helloworld &> /dev/null; then
|
||||||
dotf hello
|
helloworld
|
||||||
fi
|
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