Skip to content

Commit 94a3015

Browse files
committed
feat(cli): implement Cobra-based CLI application
- Restructure CLI from a simple file to a full Cobra command structure - Add version and config commands with proper help documentation - Implement configuration management using Viper with default config paths - Create platform-specific database in-memory implementations (Linux/Windows)
1 parent 3f58ca0 commit 94a3015

12 files changed

Lines changed: 389 additions & 5 deletions

File tree

apps/cli/.devtools.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

apps/cli/cli/cli.go

Lines changed: 0 additions & 5 deletions
This file was deleted.

apps/cli/cmd/config.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package cmd
2+
3+
import (
4+
"the-dev-tools/backend/pkg/model/massert"
5+
"the-dev-tools/backend/pkg/model/mbodyform"
6+
"the-dev-tools/backend/pkg/model/mbodyraw"
7+
"the-dev-tools/backend/pkg/model/mbodyurl"
8+
"the-dev-tools/backend/pkg/model/mcollection"
9+
"the-dev-tools/backend/pkg/model/mexampleheader"
10+
"the-dev-tools/backend/pkg/model/mexamplequery"
11+
"the-dev-tools/backend/pkg/model/mexampleresp"
12+
"the-dev-tools/backend/pkg/model/mexamplerespheader"
13+
"the-dev-tools/backend/pkg/model/mflow"
14+
"the-dev-tools/backend/pkg/model/mitemapi"
15+
"the-dev-tools/backend/pkg/model/mitemapiexample"
16+
"the-dev-tools/backend/pkg/model/mitemfolder"
17+
"the-dev-tools/backend/pkg/model/mnnode"
18+
"the-dev-tools/backend/pkg/model/mnnode/mnfor"
19+
"the-dev-tools/backend/pkg/model/mnnode/mnforeach"
20+
"the-dev-tools/backend/pkg/model/mnnode/mnif"
21+
"the-dev-tools/backend/pkg/model/mnnode/mnjs"
22+
"the-dev-tools/backend/pkg/model/mnnode/mnnoop"
23+
"the-dev-tools/backend/pkg/model/mnnode/mnrequest"
24+
)
25+
26+
type DefaultConfig struct {
27+
// collections
28+
Collections []mcollection.Collection
29+
Folders []mitemfolder.ItemFolder
30+
Endpoints []mitemapi.ItemApi
31+
Examples []mitemapiexample.ItemApiExample
32+
33+
// example sub items
34+
35+
ExampleHeaders []mexampleheader.Header
36+
ExampleQueries []mexamplequery.Query
37+
ExampleAsserts []massert.Assert
38+
39+
// body
40+
Rawbodies []mbodyraw.ExampleBodyRaw
41+
FormBodies []mbodyform.BodyForm
42+
UrlBodies []mbodyurl.BodyURLEncoded
43+
44+
// response
45+
ExampleResponses []mexampleresp.ExampleResp
46+
ExampleResponseHeaders []mexamplerespheader.ExampleRespHeader
47+
ExampleResponseAsserts []massert.Assert
48+
49+
// flows
50+
Flows []mflow.Flow
51+
52+
// Root nodes
53+
FlowNodes []mnnode.MNode
54+
55+
// Sub nodes
56+
FlowRequestNodes []mnrequest.MNRequest
57+
FlowConditionNodes []mnif.MNIF
58+
FlowNoopNodes []mnnoop.NoopNode
59+
FlowForNodes []mnfor.MNFor
60+
FlowForEachNodes []mnforeach.MNForEach
61+
FlowJSNodes []mnjs.MNJS
62+
}

apps/cli/cmd/flow.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
func init() {
10+
rootCmd.AddCommand(versionCmd)
11+
}
12+
13+
var abc = &cobra.Command{
14+
Use: "version",
15+
Short: "Print the version number of DevToolsCLI",
16+
Long: `All software has versions. This is DevToolsCLI's`,
17+
Run: func(cmd *cobra.Command, args []string) {
18+
fmt.Printf("DevToolsCLI %s\n", version)
19+
},
20+
}

apps/cli/cmd/root.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
homedir "github.com/mitchellh/go-homedir"
8+
"github.com/spf13/cobra"
9+
"github.com/spf13/viper"
10+
)
11+
12+
var rootCmd = &cobra.Command{
13+
Use: "devtoolscli",
14+
Short: "DevTools is a powerful API testing tool",
15+
Long: `DevTools is a powerful API testing tool that records your browser interactions,
16+
automatically generates requests, and seamlessly chains them for functional testing.
17+
With built-in CI integration, it streamlines API validation from development to deployment.
18+
`,
19+
Run: func(cmd *cobra.Command, args []string) {
20+
// Do Stuff Here
21+
},
22+
}
23+
24+
var cfgFilePath string
25+
26+
const (
27+
ConfigFileName = ".devtools"
28+
ConfigFileExtension = ".yaml"
29+
)
30+
31+
func init() {
32+
homePath, err := homedir.Dir()
33+
if err != nil {
34+
log.Fatal(err)
35+
}
36+
37+
cfgFilePath = fmt.Sprintf("%s/%s%s", homePath, ConfigFileName, ConfigFileExtension)
38+
39+
viper.SetDefault("data", DefaultConfig{})
40+
41+
cobra.OnInitialize(initConfig)
42+
rootCmd.PersistentFlags().StringVar(&cfgFilePath, "config", cfgFilePath, "config file (default is $HOME/.devtools.yaml)")
43+
}
44+
45+
func Execute() {
46+
if err := rootCmd.Execute(); err != nil {
47+
log.Fatalf("error executing root command: %s", err)
48+
}
49+
}
50+
51+
func initConfig() {
52+
viper.SetConfigType("yaml")
53+
// Find home directory.
54+
home, err := homedir.Dir()
55+
if err != nil {
56+
log.Fatalf("Error finding home directory: %s", err)
57+
}
58+
59+
// Search config in home directory with name ".cobra" (without extension).
60+
viper.AddConfigPath(home)
61+
viper.AddConfigPath(cfgFilePath)
62+
viper.SetConfigName(".devtools")
63+
err = viper.ReadInConfig()
64+
if err != nil {
65+
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
66+
fmt.Println("Config file not found, creating default config file")
67+
68+
// Create default config file if it doesn't exist
69+
home, _ := homedir.Dir()
70+
defaultConfigFile := home + "/.devtools.yaml"
71+
err = viper.SafeWriteConfigAs(defaultConfigFile)
72+
if err != nil {
73+
fmt.Printf("Error creating default config file: %s\n", err)
74+
}
75+
} else {
76+
fmt.Printf("error reading config file: %s\n", err)
77+
}
78+
}
79+
}

apps/cli/cmd/version.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
func init() {
10+
rootCmd.AddCommand(versionCmd)
11+
}
12+
13+
const version = "v0.0.0"
14+
15+
var versionCmd = &cobra.Command{
16+
Use: "version",
17+
Short: "Print the version number of DevToolsCLI",
18+
Long: `All software has versions. This is DevToolsCLI's`,
19+
Run: func(cmd *cobra.Command, args []string) {
20+
fmt.Printf("DevToolsCLI %s\n", version)
21+
},
22+
}

apps/cli/go.mod

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
11
module the-dev-tools/cli
22

33
go 1.24.1
4+
5+
require (
6+
github.com/mitchellh/go-homedir v1.1.0
7+
github.com/spf13/cobra v1.9.1
8+
github.com/spf13/viper v1.20.0
9+
)
10+
11+
require (
12+
github.com/fsnotify/fsnotify v1.8.0 // indirect
13+
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
14+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
15+
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
16+
github.com/sagikazarmark/locafero v0.7.0 // indirect
17+
github.com/sourcegraph/conc v0.3.0 // indirect
18+
github.com/spf13/afero v1.12.0 // indirect
19+
github.com/spf13/cast v1.7.1 // indirect
20+
github.com/spf13/pflag v1.0.6 // indirect
21+
github.com/subosito/gotenv v1.6.0 // indirect
22+
go.uber.org/atomic v1.9.0 // indirect
23+
go.uber.org/multierr v1.9.0 // indirect
24+
golang.org/x/sys v0.29.0 // indirect
25+
golang.org/x/text v0.21.0 // indirect
26+
gopkg.in/yaml.v3 v3.0.1 // indirect
27+
)

apps/cli/go.sum

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
2+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
5+
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
6+
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
7+
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
8+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
9+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
10+
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
11+
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
12+
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
13+
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
14+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
16+
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
17+
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
18+
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
19+
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
20+
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
21+
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
22+
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
23+
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
24+
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
25+
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
26+
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
27+
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
28+
github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
29+
github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
30+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
31+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
32+
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
33+
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
34+
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
35+
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
36+
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
37+
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
38+
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
39+
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
40+
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
41+
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
42+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
43+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
44+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

apps/cli/main.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
import (
4+
"the-dev-tools/cli/cmd"
5+
)
6+
7+
func main() {
8+
cmd.Execute()
9+
}

0 commit comments

Comments
 (0)