11package main
22
33import (
4+ "errors"
45 "flag"
56 "fmt"
7+ "io"
8+ "log"
69 "os"
710
811 "github.com/ctrf-io/go-ctrf-json-reporter/ctrf"
912 "github.com/ctrf-io/go-ctrf-json-reporter/reporter"
1013)
1114
12- var buildFailed bool
15+ // commandContext holds the global context of the command.
16+ //
17+ // For now, this boils down to just CLI flags and default stdin/stdout.
18+ type commandContext struct {
19+ commandFlags
1320
14- func main () {
15- var outputFile string
16- var verbose bool
17- var quiet bool
18- flag .BoolVar (& verbose , "verbose" , false , "Enable verbose output" )
19- flag .BoolVar (& verbose , "v" , false , "Enable verbose output (shorthand)" )
20- flag .BoolVar (& quiet , "quiet" , false , "Disable all log output" )
21- flag .BoolVar (& quiet , "q" , false , "Disable all log output (shorthand)" )
22- flag .StringVar (& outputFile , "output" , "ctrf-report.json" , "The output file for the test results" )
23- flag .StringVar (& outputFile , "o" , "ctrf-report.json" , "The output file for the test results (shorthand)" )
24-
25- var tempAppName , tempAppVersion , tempOSPlatform , tempOSRelease , tempOSVersion , tempBuildName , tempBuildNumber string
26-
27- flag .StringVar (& tempAppName , "appName" , "" , "The name of the application being tested." )
28- flag .StringVar (& tempAppVersion , "appVersion" , "" , "The version of the application being tested." )
29- flag .StringVar (& tempOSPlatform , "osPlatform" , "" , "The operating system platform (e.g., Windows, Linux)." )
30- flag .StringVar (& tempOSRelease , "osRelease" , "" , "The release version of the operating system." )
31- flag .StringVar (& tempOSVersion , "osVersion" , "" , "The version number of the operating system." )
32- flag .StringVar (& tempBuildName , "buildName" , "" , "The name of the build (e.g., feature branch name)." )
33- flag .StringVar (& tempBuildNumber , "buildNumber" , "" , "The build number or identifier." )
21+ reader io.Reader
22+ writer io.Writer // makes it easier to test execute() independently
23+ }
3424
35- flag .Parse ()
25+ // commandFlags stores parsed command line flags.
26+ type commandFlags struct {
27+ outputFile string
28+ verbose bool
29+ quiet bool
30+ appName string
31+ appVersion string
32+ oSPlatform string
33+ oSRelease string
34+ oSVersion string
35+ buildName string
36+ buildNumber string
37+ }
3638
37- var env * ctrf.Environment
39+ // NOTE(fredbi)
40+ // Sugggestions (future enhancements):
41+ //
42+ // - outputFile could be provided as an io.Writer: this makes the package easier to test
43+ // - outputFile is currently required but could default to stdout
44+ // - outputFile set to "-" would also mean stdout (common with unix-like tools)
45+ //
46+ // A similar approach could work for stdin, which is currently not an option, when CLI args (not flags)
47+ // could represent the input files (e.g. could be useful when used with xargs for example).
3848
39- if tempAppName != "" || tempAppVersion != "" || tempOSPlatform != "" ||
40- tempOSRelease != "" || tempOSVersion != "" || tempBuildName != "" || tempBuildNumber != "" {
41- env = & ctrf.Environment {}
49+ func main () {
50+ var ctx commandContext
51+ ctx .reader = os .Stdin
52+ ctx .writer = os .Stdout
4253
43- if tempAppName != "" {
44- env .AppName = tempAppName
45- }
46- if tempAppVersion != "" {
47- env .AppVersion = tempAppVersion
48- }
49- if tempOSPlatform != "" {
50- env .OSPlatform = tempOSPlatform
51- }
52- if tempOSRelease != "" {
53- env .OSRelease = tempOSRelease
54- }
55- if tempOSVersion != "" {
56- env .OSVersion = tempOSVersion
57- }
58- if tempBuildName != "" {
59- env .BuildName = tempBuildName
60- }
61- if tempBuildNumber != "" {
62- env .BuildNumber = tempBuildNumber
54+ registerFlags (& ctx .commandFlags )
55+
56+ if err := execute (& ctx ); err != nil {
57+ if ctx .quiet {
58+ os .Exit (1 ) // exit silently
6359 }
60+
61+ log .Fatalf ("%v" , err )
6462 }
63+ }
6564
66- effectiveVerbose := verbose && ! quiet
65+ func execute (cmd * commandContext ) error {
66+ env := ctrfEnvFromFlags (cmd )
67+ effectiveVerbose := cmd .verbose && ! cmd .quiet
6768
68- report , err := reporter .ParseTestResults (os . Stdin , effectiveVerbose , env )
69+ report , err := reporter .ParseTestResults (cmd . reader , effectiveVerbose , env )
6970 if err != nil {
70- if ! quiet {
71- _ , _ = fmt .Fprintf (os .Stderr , "Error parsing test results: %v\n " , err )
72- }
73- os .Exit (1 )
71+ return fmt .Errorf ("error parsing test results: %w" , err )
7472 }
7573
76- err = reporter .WriteReportToFile (outputFile , report )
74+ err = reporter .WriteReportToFile (cmd . outputFile , report )
7775 if err != nil {
78- if ! quiet {
79- _ , _ = fmt .Fprintf (os .Stderr , "Error writing the report to file: %v\n " , err )
80- }
81- os .Exit (1 )
76+ return fmt .Errorf ("error writing the report to file: %w" , err )
8277 }
8378
84- if ! verbose && ! quiet {
79+ if ! cmd . verbose && ! cmd . quiet { // when verbose is enabled, we already got some output
8580 buildOutput := reporter .GetBuildOutput ()
86- fmt .Println ( buildOutput )
81+ fmt .Fprint ( cmd . writer , buildOutput )
8782 }
8883
84+ var buildFailed bool
8985 if report .Results .Extra != nil {
9086 extraMap , isMap := report .Results .Extra .(map [string ]any )
9187 if ! isMap {
9288 err = fmt .Errorf ("expected a map, but got %T instead" , report .Results .Extra )
93- if ! quiet {
94- _ , _ = fmt .Fprintf (os .Stderr , "Error writing the report to file: %v\n " , err )
95- }
96- os .Exit (1 )
89+ return fmt .Errorf ("error writing the report to file: %w" , err )
9790 }
9891 if _ , ok := extraMap ["buildFail" ]; ok {
9992 buildFailed = true
@@ -108,6 +101,47 @@ func main() {
108101 }
109102
110103 if buildFailed {
111- os .Exit (1 )
104+ return errors .New ("build failed" )
105+ }
106+
107+ return nil
108+ }
109+
110+ func registerFlags (flags * commandFlags ) {
111+ flag .BoolVar (& flags .verbose , "verbose" , false , "Enable verbose output" )
112+ flag .BoolVar (& flags .verbose , "v" , false , "Enable verbose output (shorthand)" )
113+ flag .BoolVar (& flags .quiet , "quiet" , false , "Disable all log output" )
114+ flag .BoolVar (& flags .quiet , "q" , false , "Disable all log output (shorthand)" )
115+
116+ flag .StringVar (& flags .outputFile , "output" , "ctrf-report.json" , "The output file for the test results" )
117+ flag .StringVar (& flags .outputFile , "o" , "ctrf-report.json" , "The output file for the test results (shorthand)" )
118+
119+ flag .StringVar (& flags .appName , "appName" , "" , "The name of the application being tested." )
120+ flag .StringVar (& flags .appVersion , "appVersion" , "" , "The version of the application being tested." )
121+ flag .StringVar (& flags .oSPlatform , "osPlatform" , "" , "The operating system platform (e.g., Windows, Linux)." )
122+ flag .StringVar (& flags .oSRelease , "osRelease" , "" , "The release version of the operating system." )
123+ flag .StringVar (& flags .oSVersion , "osVersion" , "" , "The version number of the operating system." )
124+ flag .StringVar (& flags .buildName , "buildName" , "" , "The name of the build (e.g., feature branch name)." )
125+ flag .StringVar (& flags .buildNumber , "buildNumber" , "" , "The build number or identifier." )
126+
127+ // parsing errors result in os.Exit(1). Perhaps we should call the flagset version and capture the error instead.
128+ flag .Parse ()
129+ }
130+
131+ func ctrfEnvFromFlags (cmd * commandContext ) * ctrf.Environment {
132+ if cmd .appName == "" && cmd .appVersion == "" && cmd .oSPlatform == "" &&
133+ cmd .oSRelease == "" && cmd .oSVersion == "" && cmd .buildName == "" &&
134+ cmd .buildNumber == "" {
135+ return nil
136+ }
137+
138+ return & ctrf.Environment {
139+ AppName : cmd .appName ,
140+ AppVersion : cmd .appVersion ,
141+ OSPlatform : cmd .oSPlatform ,
142+ OSRelease : cmd .oSRelease ,
143+ OSVersion : cmd .oSVersion ,
144+ BuildName : cmd .buildName ,
145+ BuildNumber : cmd .buildNumber ,
112146 }
113147}
0 commit comments