...
1
16
17 package html
18
19 import (
20 _ "embed"
21 "fmt"
22 "html/template"
23 "io"
24 "os"
25
26 "github.com/spf13/cobra"
27 )
28
29
30 var htmlTemplate string
31
32
33 var jsBundle []byte
34
35 type flags struct {
36 OutputFile string
37 }
38
39
40 func MakeCommand() *cobra.Command {
41 flags := &flags{}
42 cmd := &cobra.Command{
43 Use: "html [coverage...]",
44 Short: "Emits an HTML file to browse coverage files.",
45 Long: `Produces a self-contained HTML file that enables browsing the provided
46 coverage files by directory. The resulting file can be distributed alone to
47 produce the same rendering (but does currently require gstatic.com to be
48 accessible).
49
50 If multiple files are provided, they will all be
51 shown in the generated HTML file, with the columns in the same order the files
52 were listed. When there are multiples columns, each column will have an arrow
53 indicating the change from the column immediately to its right.`,
54 Run: func(cmd *cobra.Command, args []string) {
55 run(flags, cmd, args)
56 },
57 }
58 cmd.Flags().StringVarP(&flags.OutputFile, "output", "o", "-", "output file")
59 return cmd
60 }
61
62 type coverageFile struct {
63 Path string `json:"path"`
64 Content string `json:"content"`
65 }
66
67 func run(flags *flags, cmd *cobra.Command, args []string) {
68 if len(args) < 1 {
69 fmt.Println("Expected at least one coverage file.")
70 _ = cmd.Usage()
71 os.Exit(2)
72 }
73
74 tpl := template.New("report")
75 tpl, err := tpl.Parse(htmlTemplate)
76 if err != nil {
77 fmt.Fprintf(os.Stderr, "Couldn't read the HTML template: %v.", err)
78 os.Exit(1)
79 }
80
81
82
83 if wd, ok := os.LookupEnv("BUILD_WORKING_DIRECTORY"); ok {
84 if err := os.Chdir(wd); err != nil {
85 fmt.Fprintf(os.Stderr, "Couldn't chdir into expected working directory.")
86 os.Exit(1)
87 }
88 }
89
90 var coverageFiles []coverageFile
91 for _, arg := range args {
92 var content []byte
93 var err error
94 if arg == "-" {
95 content, err = io.ReadAll(os.Stdin)
96 } else {
97 content, err = os.ReadFile(arg)
98 }
99 if err != nil {
100 fmt.Fprintf(os.Stderr, "Couldn't read coverage file: %v.", err)
101 os.Exit(1)
102 }
103 coverageFiles = append(coverageFiles, coverageFile{Path: arg, Content: string(content)})
104 }
105
106 outputPath := flags.OutputFile
107 var output io.Writer
108 if outputPath == "-" {
109 output = os.Stdout
110 } else {
111 f, err := os.Create(outputPath)
112 if err != nil {
113 fmt.Fprintf(os.Stderr, "Couldn't open output file: %v.", err)
114 os.Exit(1)
115 }
116 defer f.Close()
117 output = f
118 }
119
120 err = tpl.Execute(output, struct {
121 Script template.JS
122 Coverage []coverageFile
123 }{template.JS(jsBundle), coverageFiles})
124 if err != nil {
125 fmt.Fprintf(os.Stderr, "Couldn't write output file: %v.", err)
126 }
127 }
128
View as plain text