Skip to content

Commit 722d97b

Browse files
committed
Added Cobra for the Command Line Interface
1 parent e6223ba commit 722d97b

20 files changed

+612
-158
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,11 @@ var opts = godog.Options{
240240
}
241241

242242
func init() {
243+
// godog v0.10.0 (latest) and earlier
243244
godog.BindFlags("godog.", flag.CommandLine, &opts)
245+
246+
// godog v0.11.0
247+
godog.BindCommandLineFlags("godog.", &opts)
244248
}
245249

246250
func TestMain(m *testing.M) {

_examples/assert-godogs/godogs_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
var opts = godog.Options{Output: colors.Colored(os.Stdout)}
1515

1616
func init() {
17-
godog.BindFlags("godog.", flag.CommandLine, &opts)
17+
godog.BindCommandLineFlags("godog.", &opts)
1818
}
1919

2020
func TestMain(m *testing.M) {

_examples/godogs/godogs_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
var opts = godog.Options{Output: colors.Colored(os.Stdout)}
1414

1515
func init() {
16-
godog.BindFlags("godog.", flag.CommandLine, &opts)
16+
godog.BindCommandLineFlags("godog.", &opts)
1717
}
1818

1919
func TestMain(m *testing.M) {

cmd/godog/internal/cmd_build.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package internal
2+
3+
import (
4+
"fmt"
5+
"go/build"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/spf13/cobra"
10+
11+
"github.com/cucumber/godog/internal/builder"
12+
)
13+
14+
var buildOutput string
15+
var buildOutputDefault = "godog.test"
16+
17+
// CreateBuildCmd creates the build subcommand.
18+
func CreateBuildCmd() cobra.Command {
19+
if build.Default.GOOS == "windows" {
20+
buildOutputDefault += ".exe"
21+
}
22+
23+
buildCmd := cobra.Command{
24+
Use: "build",
25+
Short: "Compiles a test runner",
26+
Long: `Compiles a test runner. Command should be run from the directory of tested
27+
package and contain buildable go source.
28+
29+
The test runner can be executed with the same flags as when using godog run.`,
30+
Example: ` godog build
31+
godog build -o ` + buildOutputDefault,
32+
Run: buildCmdRunFunc,
33+
}
34+
35+
buildCmd.Flags().StringVarP(&buildOutput, "output", "o", buildOutputDefault, "compiles the test runner to the named file")
36+
37+
return buildCmd
38+
}
39+
40+
func buildCmdRunFunc(cmd *cobra.Command, args []string) {
41+
bin, err := filepath.Abs(buildOutput)
42+
if err != nil {
43+
fmt.Fprintln(os.Stderr, "could not locate absolute path for:", buildOutput, err)
44+
os.Exit(1)
45+
}
46+
47+
if err = builder.Build(bin); err != nil {
48+
fmt.Fprintln(os.Stderr, "could not build binary at:", buildOutput, err)
49+
os.Exit(1)
50+
}
51+
52+
os.Exit(0)
53+
}

cmd/godog/internal/cmd_root.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package internal
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
flag "github.com/spf13/pflag"
6+
7+
"github.com/cucumber/godog/internal/flags"
8+
)
9+
10+
var version bool
11+
var output string
12+
13+
// CreateRootCmd creates the root command.
14+
func CreateRootCmd() cobra.Command {
15+
rootCmd := cobra.Command{
16+
Use: "godog",
17+
Long: `Creates and runs test runner for the given feature files.
18+
Command should be run from the directory of tested package
19+
and contain buildable go source.`,
20+
Args: cobra.ArbitraryArgs,
21+
22+
// Deprecated: Use godog build, godog run or godog version.
23+
// This is to support the legacy direct usage of the root command.
24+
Run: func(cmd *cobra.Command, args []string) {
25+
if version {
26+
versionCmdRunFunc(cmd, args)
27+
}
28+
29+
if len(output) > 0 {
30+
buildOutput = output
31+
buildCmdRunFunc(cmd, args)
32+
}
33+
34+
runCmdRunFunc(cmd, args)
35+
},
36+
}
37+
38+
bindRootCmdFlags(rootCmd.Flags())
39+
40+
return rootCmd
41+
}
42+
43+
func bindRootCmdFlags(flagSet *flag.FlagSet) {
44+
flagSet.StringVarP(&output, "output", "o", "", "compiles the test runner to the named file")
45+
flagSet.BoolVar(&version, "version", false, "show current version")
46+
47+
flags.BindRunCmdFlags("", flagSet, &opts)
48+
49+
// Since using the root command directly is deprecated.
50+
// All flags will be hidden
51+
flagSet.MarkHidden("output")
52+
flagSet.MarkHidden("version")
53+
flagSet.MarkHidden("no-colors")
54+
flagSet.MarkHidden("concurrency")
55+
flagSet.MarkHidden("tags")
56+
flagSet.MarkHidden("format")
57+
flagSet.MarkHidden("definitions")
58+
flagSet.MarkHidden("stop-on-failure")
59+
flagSet.MarkHidden("strict")
60+
flagSet.MarkHidden("random")
61+
}

cmd/godog/internal/cmd_run.go

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package internal
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"syscall"
9+
10+
"github.com/spf13/cobra"
11+
12+
"github.com/cucumber/godog/internal/builder"
13+
"github.com/cucumber/godog/internal/flags"
14+
)
15+
16+
var opts flags.Options
17+
18+
// CreateRunCmd creates the run subcommand.
19+
func CreateRunCmd() cobra.Command {
20+
runCmd := cobra.Command{
21+
Use: "run [features]",
22+
Short: "Compiles and runs a test runner",
23+
Long: `Compiles and runs test runner for the given feature files.
24+
Command should be run from the directory of tested package and contain
25+
buildable go source.`,
26+
Example: ` godog run
27+
godog run <feature>
28+
godog run <feature> <feature>
29+
30+
Optional feature(s) to run:
31+
- dir (features/)
32+
- feature (*.feature)
33+
- scenario at specific line (*.feature:10)
34+
If no feature arguments are supplied, godog will use "features/" by default.`,
35+
Run: runCmdRunFunc,
36+
}
37+
38+
flags.BindRunCmdFlags("", runCmd.Flags(), &opts)
39+
40+
return runCmd
41+
}
42+
43+
func runCmdRunFunc(cmd *cobra.Command, args []string) {
44+
osArgs := os.Args[1:]
45+
46+
if len(osArgs) > 0 && osArgs[0] == "run" {
47+
osArgs = osArgs[1:]
48+
}
49+
50+
status, err := buildAndRunGodog(osArgs)
51+
if err != nil {
52+
fmt.Fprintln(os.Stderr, err)
53+
os.Exit(1)
54+
}
55+
56+
os.Exit(status)
57+
}
58+
59+
func buildAndRunGodog(args []string) (_ int, err error) {
60+
bin, err := filepath.Abs(buildOutputDefault)
61+
if err != nil {
62+
return 1, err
63+
}
64+
65+
if err = builder.Build(bin); err != nil {
66+
return 1, err
67+
}
68+
69+
defer os.Remove(bin)
70+
71+
return runGodog(bin, args)
72+
}
73+
74+
func runGodog(bin string, args []string) (_ int, err error) {
75+
cmd := exec.Command(bin, args...)
76+
cmd.Stdout = os.Stdout
77+
cmd.Stderr = os.Stderr
78+
cmd.Stdin = os.Stdin
79+
cmd.Env = os.Environ()
80+
81+
if err = cmd.Start(); err != nil {
82+
return 0, err
83+
}
84+
85+
if err = cmd.Wait(); err == nil {
86+
return 0, nil
87+
}
88+
89+
exiterr, ok := err.(*exec.ExitError)
90+
if !ok {
91+
return 0, err
92+
}
93+
94+
// This works on both Unix and Windows. Although package
95+
// syscall is generally platform dependent, WaitStatus is
96+
// defined for both Unix and Windows and in both cases has
97+
// an ExitStatus() method with the same signature.
98+
if st, ok := exiterr.Sys().(syscall.WaitStatus); ok {
99+
return st.ExitStatus(), nil
100+
}
101+
102+
return 1, nil
103+
}

cmd/godog/internal/cmd_version.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package internal
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/cucumber/godog"
10+
)
11+
12+
// CreateVersionCmd creates the version subcommand.
13+
func CreateVersionCmd() cobra.Command {
14+
versionCmd := cobra.Command{
15+
Use: "version",
16+
Short: "Show current version",
17+
Run: versionCmdRunFunc,
18+
DisableFlagsInUseLine: true,
19+
}
20+
21+
return versionCmd
22+
}
23+
24+
func versionCmdRunFunc(cmd *cobra.Command, args []string) {
25+
fmt.Fprintln(os.Stdout, "Godog version is:", godog.Version)
26+
os.Exit(0)
27+
}

cmd/godog/main.go

+7-98
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,15 @@
11
package main
22

33
import (
4-
"fmt"
5-
"go/build"
6-
"os"
7-
"os/exec"
8-
"path/filepath"
9-
"syscall"
10-
11-
"github.com/cucumber/godog"
12-
"github.com/cucumber/godog/colors"
13-
"github.com/cucumber/godog/internal/builder"
4+
"github.com/cucumber/godog/cmd/godog/internal"
145
)
156

16-
var parsedStatus int
17-
18-
func buildAndRun() (int, error) {
19-
var status int
20-
21-
bin, err := filepath.Abs("godog.test")
22-
if err != nil {
23-
return 1, err
24-
}
25-
if build.Default.GOOS == "windows" {
26-
bin += ".exe"
27-
}
28-
if err = builder.Build(bin); err != nil {
29-
return 1, err
30-
}
31-
defer os.Remove(bin)
32-
33-
cmd := exec.Command(bin, os.Args[1:]...)
34-
cmd.Stdout = os.Stdout
35-
cmd.Stderr = os.Stderr
36-
cmd.Stdin = os.Stdin
37-
cmd.Env = os.Environ()
38-
39-
if err = cmd.Start(); err != nil {
40-
return status, err
41-
}
42-
43-
if err = cmd.Wait(); err != nil {
44-
if exiterr, ok := err.(*exec.ExitError); ok {
45-
// The program has exited with an exit code != 0
46-
status = 1
47-
48-
// This works on both Unix and Windows. Although package
49-
// syscall is generally platform dependent, WaitStatus is
50-
// defined for both Unix and Windows and in both cases has
51-
// an ExitStatus() method with the same signature.
52-
if st, ok := exiterr.Sys().(syscall.WaitStatus); ok {
53-
status = st.ExitStatus()
54-
}
55-
return status, nil
56-
}
57-
return status, err
58-
}
59-
return status, nil
60-
}
61-
627
func main() {
63-
var vers bool
64-
var output string
65-
66-
opt := godog.Options{Output: colors.Colored(os.Stdout)}
67-
flagSet := godog.FlagSet(&opt)
68-
flagSet.BoolVar(&vers, "version", false, "Show current version.")
69-
flagSet.StringVar(&output, "o", "", "Build and output test runner executable to given target path.")
70-
flagSet.StringVar(&output, "output", "", "Build and output test runner executable to given target path.")
71-
72-
if err := flagSet.Parse(os.Args[1:]); err != nil {
73-
fmt.Fprintln(os.Stderr, err)
74-
os.Exit(1)
75-
}
76-
77-
if len(output) > 0 {
78-
bin, err := filepath.Abs(output)
79-
if err != nil {
80-
fmt.Fprintln(os.Stderr, "could not locate absolute path for:", output, err)
81-
os.Exit(1)
82-
}
83-
if err = builder.Build(bin); err != nil {
84-
fmt.Fprintln(os.Stderr, "could not build binary at:", output, err)
85-
os.Exit(1)
86-
}
87-
os.Exit(0)
88-
}
89-
90-
if vers {
91-
fmt.Fprintln(os.Stdout, "Godog version is:", godog.Version)
92-
os.Exit(0) // should it be 0?
93-
}
8+
rootCmd := internal.CreateRootCmd()
9+
buildCmd := internal.CreateBuildCmd()
10+
runCmd := internal.CreateRunCmd()
11+
versionCmd := internal.CreateVersionCmd()
9412

95-
status, err := buildAndRun()
96-
if err != nil {
97-
fmt.Fprintln(os.Stderr, err)
98-
os.Exit(1)
99-
}
100-
// it might be a case, that status might not be resolved
101-
// in some OSes. this is attempt to parse it from stderr
102-
if parsedStatus > status {
103-
status = parsedStatus
104-
}
105-
os.Exit(status)
13+
rootCmd.AddCommand(&buildCmd, &runCmd, &versionCmd)
14+
rootCmd.Execute()
10615
}

0 commit comments

Comments
 (0)