feat: add motd-assist TUI for managing dynamic MOTDs
Initial Go project built with Bubble Tea and Lip Gloss: - Live preview and management of /etc/update-motd.d scripts - Embedded presets (ubuntu-default, minimal, server-dashboard) - Bundled POSIX sh style scripts (brand header, sysinfo panel, footer) - Snapshot/restore safety around applying changes - CLI subcommands preview, apply, restore, list-presets - Unit tests for detect, sanitize, and apply logic
This commit is contained in:
commit
d56659e21a
34 changed files with 3260 additions and 0 deletions
107
internal/motd/preview.go
Normal file
107
internal/motd/preview.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package motd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"motd-assist/internal/assets"
|
||||
)
|
||||
|
||||
// scriptTimeout begrenzt die Laufzeit eines einzelnen Preview-Scripts
|
||||
// (langsame Netzwerk-Scripte wie 50-motd-news blockieren sonst die TUI).
|
||||
const scriptTimeout = 2 * time.Second
|
||||
|
||||
// PreviewScript ist ein Preview-Kandidat: entweder eine vorhandene Datei
|
||||
// (Path) oder eine noch nicht installierte gebündelte Vorlage (Source).
|
||||
type PreviewScript struct {
|
||||
Name string
|
||||
Path string
|
||||
Source string
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// Render führt die aktivierten Scripte in Reihenfolge aus und liefert den
|
||||
// bereinigten MOTD-Output. Fehler einzelner Scripte werden als dezente
|
||||
// Hinweiszeile eingebaut statt die ganze Preview abbrechen.
|
||||
func Render(scripts []PreviewScript, width int) string {
|
||||
var parts []string
|
||||
for _, s := range scripts {
|
||||
if !s.Enabled {
|
||||
continue
|
||||
}
|
||||
path := s.Path
|
||||
if path == "" {
|
||||
if s.Source == "" {
|
||||
continue
|
||||
}
|
||||
staged, cleanup, err := stagePreview(s.Source, s.Name)
|
||||
if err != nil {
|
||||
parts = append(parts, dimLine(fmt.Sprintf("%s: %v", s.Name, err)))
|
||||
continue
|
||||
}
|
||||
path = staged
|
||||
defer cleanup()
|
||||
}
|
||||
addPart(&parts, runScript(path, width), width)
|
||||
}
|
||||
return strings.Join(parts, "\n\n")
|
||||
}
|
||||
|
||||
func addPart(parts *[]string, raw string, width int) {
|
||||
if text := strings.TrimSpace(Sanitize(raw, width)); text != "" {
|
||||
*parts = append(*parts, text)
|
||||
}
|
||||
}
|
||||
|
||||
func runScript(path string, width int) string {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), scriptTimeout)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, path)
|
||||
cmd.Env = append(os.Environ(),
|
||||
fmt.Sprintf("COLUMNS=%d", width),
|
||||
"TERM=xterm-256color",
|
||||
)
|
||||
cmd.Dir = "/"
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return dimLine(fmt.Sprintf("%s: Timeout", filepath.Base(path)))
|
||||
}
|
||||
if buf.Len() == 0 {
|
||||
return dimLine(fmt.Sprintf("%s: nicht ausführbar (%v)", filepath.Base(path), err))
|
||||
}
|
||||
// Exit-Status != 0 ist bei MOTD-Scripten üblich; Output behalten.
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// stagePreview kopiert eine gebündelte Vorlage in ein Temporärverzeichnis,
|
||||
// damit die Live-Vorschau sie vor der Installation zeigen kann.
|
||||
func stagePreview(source, name string) (string, func(), error) {
|
||||
content, ok := assets.ScriptContent(source)
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("unbekannte Vorlage %q", source)
|
||||
}
|
||||
dir, err := os.MkdirTemp("", "motd-assist-")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
path := filepath.Join(dir, name)
|
||||
if err := os.WriteFile(path, []byte(content), 0o755); err != nil {
|
||||
os.RemoveAll(dir)
|
||||
return "", nil, err
|
||||
}
|
||||
return path, func() { os.RemoveAll(dir) }, nil
|
||||
}
|
||||
|
||||
func dimLine(s string) string { return "\x1b[2m" + s + "\x1b[0m" }
|
||||
Loading…
Add table
Add a link
Reference in a new issue