mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: Add UI port for guard
This commit is contained in:
@@ -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(),
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user