github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/gopherage/cmd/html/html.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package html 18 19 import ( 20 "fmt" 21 "html/template" 22 "io" 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 27 "github.com/spf13/cobra" 28 ) 29 30 type flags struct { 31 OutputFile string 32 } 33 34 // MakeCommand returns a `diff` command. 35 func MakeCommand() *cobra.Command { 36 flags := &flags{} 37 cmd := &cobra.Command{ 38 Use: "html [coverage...]", 39 Short: "Emits an HTML file to browse coverage files.", 40 Long: `Produces a self-contained HTML file that enables browsing the provided 41 coverage files by directory. The resulting file can be distributed alone to 42 produce the same rendering (but does currently require gstatic.com to be 43 accessible). 44 45 If multiple files are provided, they will all be 46 shown in the generated HTML file, with the columns in the same order the files 47 were listed. When there are multiples columns, each column will have an arrow 48 indicating the change from the column immediately to its right.`, 49 Run: func(cmd *cobra.Command, args []string) { 50 run(flags, cmd, args) 51 }, 52 } 53 cmd.Flags().StringVarP(&flags.OutputFile, "output", "o", "-", "output file") 54 return cmd 55 } 56 57 type coverageFile struct { 58 Path string `json:"path"` 59 Content string `json:"content"` 60 } 61 62 func run(flags *flags, cmd *cobra.Command, args []string) { 63 if len(args) < 1 { 64 fmt.Println("Expected at least one coverage file.") 65 cmd.Usage() 66 os.Exit(2) 67 } 68 69 // This path assumes we're being run using bazel. 70 resourceDir := "gopherage/cmd/html/static" 71 if _, err := os.Stat(resourceDir); os.IsNotExist(err) { 72 fmt.Fprintf(os.Stderr, "Resource directory does not exist.") 73 os.Exit(1) 74 } 75 76 tpl, err := template.ParseFiles(filepath.Join(resourceDir, "browser.html")) 77 if err != nil { 78 fmt.Fprintf(os.Stderr, "Couldn't read the HTML template: %v.", err) 79 os.Exit(1) 80 } 81 script, err := ioutil.ReadFile(filepath.Join(resourceDir, "browser_bundle.es6.js")) 82 if err != nil { 83 fmt.Fprintf(os.Stderr, "Couldn't read JavaScript: %v.", err) 84 os.Exit(1) 85 } 86 87 // If we're under bazel, move into BUILD_WORKING_DIRECTORY so that manual 88 // invocations of bazel run are less confusing. 89 if wd, ok := os.LookupEnv("BUILD_WORKING_DIRECTORY"); ok { 90 if err := os.Chdir(wd); err != nil { 91 fmt.Fprintf(os.Stderr, "Couldn't chdir into expected working directory.") 92 os.Exit(1) 93 } 94 } 95 96 var coverageFiles []coverageFile 97 for _, arg := range args { 98 var content []byte 99 var err error 100 if arg == "-" { 101 content, err = ioutil.ReadAll(os.Stdin) 102 } else { 103 content, err = ioutil.ReadFile(arg) 104 } 105 if err != nil { 106 fmt.Fprintf(os.Stderr, "Couldn't read coverage file: %v.", err) 107 os.Exit(1) 108 } 109 coverageFiles = append(coverageFiles, coverageFile{Path: arg, Content: string(content)}) 110 } 111 112 outputPath := flags.OutputFile 113 var output io.Writer 114 if outputPath == "-" { 115 output = os.Stdout 116 } else { 117 f, err := os.Create(outputPath) 118 if err != nil { 119 fmt.Fprintf(os.Stderr, "Couldn't open output file: %v.", err) 120 os.Exit(1) 121 } 122 defer f.Close() 123 output = f 124 } 125 126 err = tpl.Execute(output, struct { 127 Script template.JS 128 Coverage []coverageFile 129 }{template.JS(script), coverageFiles}) 130 if err != nil { 131 fmt.Fprintf(os.Stderr, "Couldn't write output file: %v.", err) 132 } 133 }