mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* update the go.sum Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * migrate most of the files to dry errors Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * update the rest of the files Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * fixs the review comments Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * chores Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> --------- Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com>
208 lines
4.9 KiB
Go
208 lines
4.9 KiB
Go
package sandbox
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/safedep/pmg/internal/ui"
|
|
pmgsandbox "github.com/safedep/pmg/sandbox"
|
|
"github.com/safedep/dry/usefulerror"
|
|
"github.com/safedep/pmg/errcodes"
|
|
"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.NewUsefulError().
|
|
WithCode(errcodes.InvalidArgument).
|
|
WithHumanError(message).
|
|
WithHelp(help).
|
|
Wrap(errors.New(message))
|
|
}
|
|
|
|
func notFoundError(message, help string) error {
|
|
return usefulerror.NewUsefulError().
|
|
WithCode(errcodes.NotFound).
|
|
WithHumanError(message).
|
|
WithHelp(help).
|
|
Wrap(errors.New(message))
|
|
}
|
|
|
|
// Idempotent: returns err unchanged when nil or already useful, so call
|
|
// sites can apply it without losing more precise pre-classified errors.
|
|
func wrapUseful(err error, code, help string) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if _, ok := usefulerror.AsUsefulError(err); ok {
|
|
return err
|
|
}
|
|
return usefulerror.NewUsefulError().
|
|
WithCode(code).
|
|
WithHumanError(err.Error()).
|
|
WithHelp(help).
|
|
Wrap(err)
|
|
}
|
|
|
|
func profileLoadError(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if _, ok := usefulerror.AsUsefulError(err); ok {
|
|
return err
|
|
}
|
|
switch {
|
|
case errors.Is(err, pmgsandbox.ErrProfileNotFound):
|
|
return wrapUseful(err, errcodes.NotFound,
|
|
"Use `pmg sandbox profile list` to see available profiles, or pass an existing profile YAML path.")
|
|
case errors.Is(err, pmgsandbox.ErrProfileInvalid):
|
|
return wrapUseful(err, errcodes.InvalidArgument,
|
|
"Check the profile YAML for syntax/schema issues and verify any 'inherits:' parent name.")
|
|
}
|
|
return wrapUseful(err, errcodes.Unknown,
|
|
"Failed to load the sandbox profile. Run with --verbose for the underlying cause.")
|
|
}
|
|
|
|
func registryInitError(err error) error {
|
|
return wrapUseful(err, ioErrorCode(err, errcodes.Unknown),
|
|
"Failed to initialise the sandbox profile registry. Run with --verbose for details.")
|
|
}
|
|
|
|
func ioErrorCode(err error, fallback string) string {
|
|
switch {
|
|
case errors.Is(err, fs.ErrPermission):
|
|
return errcodes.PermissionDenied
|
|
case errors.Is(err, fs.ErrNotExist):
|
|
return errcodes.NotFound
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
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):]
|
|
}
|