feat: Add UI port for guard

This commit is contained in:
abhisek
2025-05-14 14:01:20 +05:30
parent 9aa2d97cd5
commit 723b6142bc
7 changed files with 187 additions and 4 deletions
+17
View File
@@ -0,0 +1,17 @@
package ui
import "github.com/fatih/color"
type TerminalColors struct {
Red func(format string, a ...interface{}) string
Yellow func(format string, a ...interface{}) string
Cyan func(format string, a ...interface{}) string
Green func(format string, a ...interface{}) string
}
var Colors = TerminalColors{
Red: color.New(color.FgRed, color.Bold).SprintfFunc(),
Yellow: color.New(color.FgYellow).SprintfFunc(),
Cyan: color.New(color.FgCyan).SprintfFunc(),
Green: color.New(color.FgGreen).SprintfFunc(),
}
+45
View File
@@ -0,0 +1,45 @@
package ui
import (
"fmt"
"time"
)
var spinnerChan chan bool
func StartSpinner(msg string) {
style := `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏`
frames := []rune(style)
length := len(frames)
spinnerChan = make(chan bool)
ticker := time.NewTicker(100 * time.Millisecond)
go func() {
pos := 0
for {
select {
case <-spinnerChan:
ticker.Stop()
return
case <-ticker.C:
fmt.Printf("\r%s ... %s", msg, string(frames[pos%length]))
pos += 1
}
}
}()
}
func StopSpinner() {
// Gracefully handle the case where the spinner is already stopped
// and the channel is closed, yet client code calls StopSpinner() again.
defer func() {
_ = recover()
}()
close(spinnerChan)
fmt.Printf("\r")
fmt.Println()
}
+87
View File
@@ -0,0 +1,87 @@
package ui
import (
"fmt"
"os"
"strings"
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
)
// The UI is internal to PMG and opinionated for the CLI.
// It is not intended to be used outside of PMG.
type VerbosityLevel int
const (
// PMG is hidden from the user except for errors
// and when malicious packages are detected
VerbosityLevelSilent VerbosityLevel = iota
// Show minimal status updates
VerbosityLevelNormal
// Show verbose status updates and information including
// information about malicious packages
VerbosityLevelVerbose
)
var verbosityLevel VerbosityLevel = VerbosityLevelNormal
func SetVerbosityLevel(level VerbosityLevel) {
verbosityLevel = level
}
func ClearStatus() {
StopSpinner()
fmt.Print("\r")
}
func Block() error {
StopSpinner()
fmt.Println(Colors.Red("❌ Malicious packages detected, installation blocked!"))
os.Exit(1)
return nil
}
func SetStatus(status string) {
if verbosityLevel == VerbosityLevelSilent {
return
}
StopSpinner()
fmt.Print("\r", Colors.Green(status), " ")
StartSpinner(status)
}
func GetConfirmationOnMalware(malwarePackages []*packagev1.PackageVersion) (bool, error) {
StopSpinner()
fmt.Println(Colors.Red("🚨 Malicious packages detected:"))
for _, pkg := range malwarePackages {
fmt.Println(" ⚠️ ", Colors.Red(fmt.Sprintf("%s@%s", pkg.Package.Name, pkg.Version)))
}
fmt.Println()
fmt.Print(Colors.Yellow("Do you want to continue with the installation? (y/N) "))
var response string
// We don't care about the error here because we will return false
// if the user doesn't provide a valid response
_, _ = fmt.Scanln(&response)
if len(response) == 0 {
return false, nil
}
response = strings.ToLower(response)
if response == "y" || response == "yes" || response[0] == 'y' {
return true, nil
}
return false, nil
}