pkg/sdk/cmdutil/bug.go (143 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package cmdutil import ( "bytes" "fmt" "io" "os" "path/filepath" "regexp" "runtime" "github.com/elastic/harp/build/version" exec "golang.org/x/sys/execabs" ) // BugReport generates a bug report body // //nolint:godox // Bug not allow in documentation func BugReport() string { var buf bytes.Buffer buf.WriteString(bugHeader) printGoVersion(&buf) buf.WriteString("### Does this issue reproduce with the latest release?\n\n\n") printEnvDetails(&buf) printAppDetails(&buf) buf.WriteString(bugFooter) return buf.String() } const bugHeader = `<!-- Please answer these questions before submitting your issue. Thanks! --> ` const bugFooter = `### What did you do? <!-- If possible, provide a recipe for reproducing the error. A complete runnable program is good. --> ### What did you expect to see? ### What did you see instead? ` func printGoVersion(w io.Writer) { fmt.Fprintf(w, "### What version of Go are you using (`go version`)?\n\n") fmt.Fprintf(w, "<pre>\n") fmt.Fprintf(w, "$ go version\n") printCmdOut(w, "", "go", "version") fmt.Fprintf(w, "</pre>\n") fmt.Fprintf(w, "\n") } func printEnvDetails(w io.Writer) { fmt.Fprintf(w, "### What operating system and processor architecture are you using (`go env`)?\n\n") fmt.Fprintf(w, "<details><summary><code>go env</code> Output</summary><br><pre>\n") fmt.Fprintf(w, "$ go env\n") printCmdOut(w, "", "go", "env") printGoDetails(w) printOSDetails(w) printCDetails(w) fmt.Fprintf(w, "</pre></details>\n\n") } func printGoDetails(w io.Writer) { printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin", "go"), "version") printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin", "go"), "tool", "compile", "-V") } func printOSDetails(w io.Writer) { switch runtime.GOOS { case "darwin": printCmdOut(w, "uname -v: ", "uname", "-v") printCmdOut(w, "", "sw_vers") case "linux": printCmdOut(w, "uname -sr: ", "uname", "-sr") printCmdOut(w, "", "lsb_release", "-a") printGlibcVersion(w) case "openbsd", "netbsd", "freebsd", "dragonfly": printCmdOut(w, "uname -v: ", "uname", "-v") case "illumos", "solaris": // Be sure to use the OS-supplied uname, in "/usr/bin": printCmdOut(w, "uname -srv: ", "/usr/bin/uname", "-srv") out, err := os.ReadFile("/etc/release") if err == nil { fmt.Fprintf(w, "/etc/release: %s\n", out) } } } func printAppDetails(w io.Writer) { bi := version.NewInfo() fmt.Fprintf(w, "### What version of Secret are you using (`harp version`)?\n\n") fmt.Fprintf(w, "<pre>\n") fmt.Fprintf(w, "$ harp version\n") fmt.Fprintf(w, "%s\n", bi.String()) fmt.Fprintf(w, "</pre>\n") fmt.Fprintf(w, "\n") } func printCDetails(w io.Writer) { printCmdOut(w, "lldb --version: ", "lldb", "--version") cmd := exec.Command("gdb", "--version") out, err := cmd.Output() if err == nil { // There's apparently no combination of command line flags // to get gdb to spit out its version without the license and warranty. // Print up to the first newline. fmt.Fprintf(w, "gdb --version: %s\n", firstLine(out)) } } // printCmdOut prints the output of running the given command. // It ignores failures; 'go bug' is best effort. func printCmdOut(w io.Writer, prefix, path string, args ...string) { cmd := exec.Command(path, args...) out, err := cmd.Output() if err != nil { return } fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out)) } // firstLine returns the first line of a given byte slice. func firstLine(buf []byte) []byte { idx := bytes.IndexByte(buf, '\n') if idx > 0 { buf = buf[:idx] } return bytes.TrimSpace(buf) } // printGlibcVersion prints information about the glibc version. // It ignores failures. func printGlibcVersion(w io.Writer) { tempdir := os.TempDir() if tempdir == "" { return } src := []byte(`int main() {}`) srcfile := filepath.Join(tempdir, "go-bug.c") outfile := filepath.Join(tempdir, "go-bug") err := os.WriteFile(srcfile, src, 0o600) if err != nil { return } defer os.Remove(srcfile) cmd := exec.Command("gcc", "-o", outfile, srcfile) if _, err = cmd.CombinedOutput(); err != nil { return } defer os.Remove(outfile) cmd = exec.Command("ldd", outfile) out, err := cmd.CombinedOutput() if err != nil { return } re := regexp.MustCompile(`libc\.so[^ ]* => ([^ ]+)`) m := re.FindStringSubmatch(string(out)) if m == nil { return } //nolint:gosec // controlled input cmd = exec.Command(m[1]) out, err = cmd.Output() if err != nil { return } fmt.Fprintf(w, "%s: %s\n", m[1], firstLine(out)) // print another line (the one containing version string) in case of musl libc if idx := bytes.IndexByte(out, '\n'); bytes.Contains(out, []byte("musl")) { fmt.Fprintf(w, "%s\n", firstLine(out[idx+1:])) } }