github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/internal/commands/report.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 "regexp" 10 "runtime" 11 "strings" 12 "text/template" 13 14 "github.com/spf13/cobra" 15 16 "github.com/buildpacks/pack/internal/build" 17 "github.com/buildpacks/pack/internal/builder" 18 "github.com/buildpacks/pack/pkg/logging" 19 ) 20 21 func Report(logger logging.Logger, version, cfgPath string) *cobra.Command { 22 var explicit bool 23 24 cmd := &cobra.Command{ 25 Use: "report", 26 Args: cobra.NoArgs, 27 Short: "Display useful information for reporting an issue", 28 Example: "pack report", 29 RunE: logError(logger, func(cmd *cobra.Command, args []string) error { 30 var buf bytes.Buffer 31 err := generateOutput(&buf, version, cfgPath, explicit) 32 if err != nil { 33 return err 34 } 35 36 logger.Info(buf.String()) 37 38 return nil 39 }), 40 } 41 42 cmd.Flags().BoolVarP(&explicit, "explicit", "e", false, "Print config without redacting information") 43 AddHelpFlag(cmd, "report") 44 return cmd 45 } 46 47 func generateOutput(writer io.Writer, version, cfgPath string, explicit bool) error { 48 tpl := template.Must(template.New("").Parse(`Pack: 49 Version: {{ .Version }} 50 OS/Arch: {{ .OS }}/{{ .Arch }} 51 52 Default Lifecycle Version: {{ .DefaultLifecycleVersion }} 53 54 Supported Platform APIs: {{ .SupportedPlatformAPIs }} 55 56 Config: 57 {{ .Config -}}`)) 58 59 configData := "" 60 if data, err := os.ReadFile(filepath.Clean(cfgPath)); err != nil { 61 configData = fmt.Sprintf("(no config file found at %s)", cfgPath) 62 } else { 63 var padded strings.Builder 64 65 for _, line := range strings.Split(string(data), "\n") { 66 if !explicit { 67 line = sanitize(line) 68 } 69 _, _ = fmt.Fprintf(&padded, " %s\n", line) 70 } 71 configData = strings.TrimRight(padded.String(), " \n") 72 } 73 74 platformAPIs := strings.Join(build.SupportedPlatformAPIVersions.AsStrings(), ", ") 75 76 return tpl.Execute(writer, map[string]string{ 77 "Version": version, 78 "OS": runtime.GOOS, 79 "Arch": runtime.GOARCH, 80 "DefaultLifecycleVersion": builder.DefaultLifecycleVersion, 81 "SupportedPlatformAPIs": platformAPIs, 82 "Config": configData, 83 }) 84 } 85 86 func sanitize(line string) string { 87 re := regexp.MustCompile(`"(.*?)"`) 88 redactedString := `"[REDACTED]"` 89 sensitiveFields := []string{ 90 "default-builder-image", 91 "image", 92 "mirrors", 93 "name", 94 "url", 95 } 96 for _, field := range sensitiveFields { 97 if strings.HasPrefix(strings.TrimSpace(line), field) { 98 return re.ReplaceAllString(line, redactedString) 99 } 100 } 101 102 return line 103 }