mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* feat: add sandbox DX commands * fix: Linter errors * fix: Sandbox deny log parsing * fix: Sandbox docs * refactor: Maintain SSOT across pkg dependencies * fix: Linter errors
156 lines
3.4 KiB
Go
156 lines
3.4 KiB
Go
package sandbox
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/safedep/pmg/internal/ui"
|
|
pmgsandbox "github.com/safedep/pmg/sandbox"
|
|
"github.com/safedep/pmg/usefulerror"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
const ExitCodeProbeFailure = 2
|
|
|
|
var sandboxErrorExit = func(_ *cobra.Command, err error) error {
|
|
type exitCoder interface{ ExitCode() int }
|
|
if ec, ok := err.(exitCoder); ok {
|
|
ui.ErrorExitWithCode(err, ec.ExitCode())
|
|
return nil
|
|
}
|
|
|
|
ui.ErrorExit(err)
|
|
return nil
|
|
}
|
|
|
|
var validDrivers = map[pmgsandbox.DriverName]struct{}{
|
|
pmgsandbox.DriverSeatbelt: {},
|
|
pmgsandbox.DriverBubblewrap: {},
|
|
pmgsandbox.DriverLandlock: {},
|
|
}
|
|
|
|
func validateDriver(name string) error {
|
|
if name == "" {
|
|
return nil
|
|
}
|
|
if _, ok := validDrivers[pmgsandbox.DriverName(name)]; !ok {
|
|
return invalidArgumentError(
|
|
fmt.Sprintf("unknown driver %q", name),
|
|
"Use one of: seatbelt, bubblewrap, landlock",
|
|
)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func invalidArgumentError(message, help string) error {
|
|
return usefulerror.Useful().
|
|
WithCode(usefulerror.ErrCodeInvalidArgument).
|
|
WithHumanError(message).
|
|
WithHelp(help).
|
|
Wrap(errors.New(message))
|
|
}
|
|
|
|
func notFoundError(message, help string) error {
|
|
return usefulerror.Useful().
|
|
WithCode(usefulerror.ErrCodeNotFound).
|
|
WithHumanError(message).
|
|
WithHelp(help).
|
|
Wrap(errors.New(message))
|
|
}
|
|
|
|
func writeJSONIndent(out io.Writer, v any) error {
|
|
enc := json.NewEncoder(out)
|
|
enc.SetIndent("", " ")
|
|
return enc.Encode(v)
|
|
}
|
|
|
|
// renderTable prints rows with ANSI-aware column alignment. The first row is
|
|
// treated as a header. After each data row, optional continuation lines may be
|
|
// emitted via the after callback (passed the data row index; -1 for header).
|
|
func renderTable(out io.Writer, rows [][]string, after func(rowIdx int) error) error {
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
cols := len(rows[0])
|
|
widths := make([]int, cols)
|
|
for _, row := range rows {
|
|
for i, cell := range row {
|
|
if w := visibleWidth(cell); w > widths[i] {
|
|
widths[i] = w
|
|
}
|
|
}
|
|
}
|
|
for rIdx, row := range rows {
|
|
for i, cell := range row {
|
|
if i == cols-1 {
|
|
if _, err := fmt.Fprint(out, cell); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
pad := widths[i] - visibleWidth(cell)
|
|
if _, err := fmt.Fprint(out, cell, strings.Repeat(" ", pad+2)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err := fmt.Fprintln(out); err != nil {
|
|
return err
|
|
}
|
|
if after != nil {
|
|
dataIdx := rIdx - 1
|
|
if err := after(dataIdx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// firstColumnIndent returns blanks the width of the first column plus the
|
|
// two-space padding renderTable uses, for continuation-line alignment.
|
|
func firstColumnIndent(rows [][]string) string {
|
|
if len(rows) == 0 {
|
|
return ""
|
|
}
|
|
w := 0
|
|
for _, row := range rows {
|
|
if v := visibleWidth(row[0]); v > w {
|
|
w = v
|
|
}
|
|
}
|
|
return strings.Repeat(" ", w+2)
|
|
}
|
|
|
|
var ansiEscapeRe = regexp.MustCompile(`\x1b\[[0-9;]*[A-Za-z]`)
|
|
|
|
// visibleWidth returns the printable width of s with ANSI escape sequences
|
|
// stripped — text/tabwriter counts escape bytes as visible chars, misaligning
|
|
// colored cells.
|
|
func visibleWidth(s string) int {
|
|
return len(ansiEscapeRe.ReplaceAllString(s, ""))
|
|
}
|
|
|
|
func truncate(s string, n int) string {
|
|
if len(s) <= n {
|
|
return s
|
|
}
|
|
if n <= 3 {
|
|
return s[:n]
|
|
}
|
|
return s[:n-3] + "..."
|
|
}
|
|
|
|
func truncateLeft(s string, n int) string {
|
|
if len(s) <= n {
|
|
return s
|
|
}
|
|
if n <= 3 {
|
|
return s[len(s)-n:]
|
|
}
|
|
return "..." + s[len(s)-(n-3):]
|
|
}
|