Skip to content

Commit 21b5292

Browse files
authored
Improvements for GoVPP CLI (#135)
* Improvements for GoVPP CLI - added new linter rule MessageSameStatus - fixed issue with cache of cloned repository - added option --targz for vppapi export command - fixed issues with color mode when not using terminal - added option to vppapi diff command to include comment differences - added --paths option to filter out specific API files or paths Signed-off-by: Ondrej Fabry <[email protected]> * Remove test Signed-off-by: Ondrej Fabry <[email protected]> * Resolve review feedback Signed-off-by: Ondrej Fabry <[email protected]> --------- Signed-off-by: Ondrej Fabry <[email protected]>
1 parent 14d176d commit 21b5292

24 files changed

+564
-357
lines changed

binapigen/generator.go

-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ type Generator struct {
5050
opts Options
5151

5252
vppapiSchema *vppapi.Schema
53-
//apiFiles []vppapi.File
54-
//vppVersion string
5553

5654
Files []*File
5755
FilesByName map[string]*File

binapigen/vppapi.go

+9
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ func normalizeImport(imp string) string {
9494
return imp
9595
}
9696

97+
// SortFilesByName sorts list of files by their name.
98+
func SortFilesByName(apifiles []vppapi.File) {
99+
sort.SliceStable(apifiles, func(i, j int) bool {
100+
a := apifiles[i]
101+
b := apifiles[j]
102+
return a.Name < b.Name
103+
})
104+
}
105+
97106
// SortFilesByImports sorts list of files by their imports.
98107
func SortFilesByImports(apifiles []vppapi.File) {
99108
dependsOn := func(file *vppapi.File, dep string) bool {

binapigen/vppapi/input.go

+16-12
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const (
106106
cacheDir = "./.cache"
107107
)
108108

109-
func cloneRepoLocally(repo string, commit string, depth int) (string, error) {
109+
func cloneRepoLocally(repo string, commit string, branch string, depth int) (string, error) {
110110
repoPath := strings.ReplaceAll(repo, "/", "_")
111111
repoPath = strings.ReplaceAll(repoPath, ":", "_")
112112
cachePath := filepath.Join(cacheDir, repoPath)
@@ -116,36 +116,40 @@ func cloneRepoLocally(repo string, commit string, depth int) (string, error) {
116116
if err := os.MkdirAll(cacheDir, 0755); err != nil {
117117
return "", fmt.Errorf("failed to create cache directory: %w", err)
118118
}
119-
logrus.Tracef("Cloning repository %q", repo)
120119

121-
var args []string
120+
args := []string{"--single-branch"}
122121
if depth > 0 {
123122
args = append(args, fmt.Sprintf("--depth=%d", depth))
124123
}
125124
args = append(args, repo, cachePath)
125+
logrus.Debugf("cloning repo: %v", args)
126126
cmd := exec.Command("git", append([]string{"clone"}, args...)...)
127127
if output, err := cmd.CombinedOutput(); err != nil {
128128
return "", fmt.Errorf("failed to clone repository: %w\nOutput: %s", err, output)
129129
}
130130
} else if err != nil {
131131
return "", fmt.Errorf("failed to check if cache exists: %w", err)
132-
} else {
133-
logrus.Debugf("Fetching %q", commit)
134-
cmd := exec.Command("git", "fetch", "origin", commit)
135-
cmd.Dir = cachePath
136-
if output, err := cmd.CombinedOutput(); err != nil {
137-
return "", fmt.Errorf("failed to fetch repository: %w\nOutput: %s", err, output)
138-
}
132+
}
133+
logrus.Debugf("local repo dir: %q, fetching %q", cachePath, commit)
134+
135+
cmd := exec.Command("git", "fetch", "-f", "origin", commit)
136+
cmd.Dir = cachePath
137+
if output, err := cmd.CombinedOutput(); err != nil {
138+
return "", fmt.Errorf("failed to fetch repository: %w\nOutput: %s", err, output)
139139
}
140140

141141
// Resolve the commit hash for the given branch/tag
142-
commitHash, err := resolveCommitHash(cachePath, commit)
142+
ref := commit
143+
if branch != "" {
144+
ref = "origin/" + branch
145+
}
146+
commitHash, err := resolveCommitHash(cachePath, ref)
143147
if err != nil {
144148
return "", fmt.Errorf("failed to resolve commit hash: %w", err)
145149
}
146150

147151
// Check out the repository at the resolved commit
148-
cmd := exec.Command("git", "checkout", commitHash)
152+
cmd = exec.Command("git", "checkout", commitHash)
149153
cmd.Dir = cachePath
150154
if output, err := cmd.CombinedOutput(); err != nil {
151155
return "", fmt.Errorf("failed to check out repository: %w\nOutput: %s", err, output)

binapigen/vppapi/input_test.go

+1-11
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,11 @@ func Test_runCommandInRepo(t *testing.T) {
4040
},
4141
wantErr: false,
4242
},
43-
{
44-
name: "tag",
45-
args: args{
46-
repo: "https://github.com/FDio/vpp.git",
47-
commit: "v23.02",
48-
command: "bash",
49-
args: []string{"-exc", "bash ./src/scripts/version; make json-api-files && ls ./build-root/install-vpp-native/vpp/share/vpp/api"},
50-
},
51-
wantErr: false,
52-
},
5343
}
5444
for _, tt := range tests {
5545
t.Run(tt.name, func(t *testing.T) {
5646
//, tt.args.command, tt.args.args...
57-
_, err := cloneRepoLocally(tt.args.repo, tt.args.commit, 0)
47+
_, err := cloneRepoLocally(tt.args.repo, tt.args.commit, "", 0)
5848
if (err != nil) != tt.wantErr {
5949
t.Errorf("cloneRepoLocally() error = %v, wantErr %v", err, tt.wantErr)
6050
}

binapigen/vppapi/source.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (input *InputRef) Retrieve() (*VppInput, error) {
6565
return nil, fmt.Errorf("invalid path in input reference")
6666
}
6767

68-
logrus.Tracef("retrieving input:\n%v", input)
68+
logrus.Tracef("retrieving input: %+v", input)
6969

7070
switch input.Format {
7171
case FormatNone:
@@ -90,7 +90,7 @@ func (input *InputRef) Retrieve() (*VppInput, error) {
9090
return nil, fmt.Errorf("cannot set both branch and tag")
9191
} else if branch != "" || tag != "" {
9292
if ref != "" {
93-
return nil, fmt.Errorf("cannot set ref if branch or tag is set")
93+
return nil, fmt.Errorf("cannot set rev if branch or tag is set")
9494
}
9595
if branch != "" {
9696
ref = branch
@@ -115,7 +115,7 @@ func (input *InputRef) Retrieve() (*VppInput, error) {
115115

116116
logrus.Debugf("updating local repo %s to %s", input.Path, commit)
117117

118-
repoDir, err := cloneRepoLocally(input.Path, commit, cloneDepth)
118+
repoDir, err := cloneRepoLocally(input.Path, commit, branch, cloneDepth)
119119
if err != nil {
120120
return nil, err
121121
}

binapigen/vppapi/util.go

+23-9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"os"
2222
"os/exec"
2323
"path"
24+
"path/filepath"
2425
"strings"
2526
"time"
2627

@@ -53,7 +54,10 @@ func ResolveApiDir(dir string) string {
5354
// check if the API directory exists
5455
_, err := os.Stat(apiDirPath)
5556
if err == nil {
56-
logrus.Tracef("returning %q as api dir", localVPPBuildApiDir)
57+
logrus.Tracef("api dir %q exists, running 'make json-api-files'", localVPPBuildApiDir)
58+
if err := makeJsonApiFiles(dir); err != nil {
59+
logrus.Warnf("make json-api-files failed: %v", err)
60+
}
5761
return apiDirPath
5862
} else if errors.Is(err, os.ErrNotExist) {
5963
logrus.Tracef("api dir %q does not exist, running 'make json-api-files'", localVPPBuildApiDir)
@@ -94,12 +98,6 @@ func makeJsonApiFiles(dir string) error {
9498
//
9599
// Version resolved here can be overriden by setting VPP_VERSION env var.
96100
func ResolveVPPVersion(apidir string) string {
97-
// check env variable override
98-
if ver := os.Getenv(VPPVersionEnvVar); ver != "" {
99-
logrus.Debugf("VPP version was manually set to %q via %s env var", ver, VPPVersionEnvVar)
100-
return ver
101-
}
102-
103101
// check if inside VPP repo
104102
repoDir, err := findGitRepoRootDir(apidir)
105103
if err != nil {
@@ -118,12 +116,28 @@ func ResolveVPPVersion(apidir string) string {
118116
// try to read VPP_VERSION file
119117
data, err := os.ReadFile(path.Join(repoDir, "VPP_VERSION"))
120118
if err == nil {
121-
return strings.TrimSpace(string(data))
119+
ver := strings.TrimSpace(string(data))
120+
logrus.Debugf("VPP version was resolved to %q from VPP_VERSION file", ver)
121+
return ver
122122
}
123123
}
124124

125+
// try to read VPP_VERSION file
126+
data, err := os.ReadFile(path.Join(apidir, "VPP_VERSION"))
127+
if err == nil {
128+
ver := strings.TrimSpace(string(data))
129+
logrus.Debugf("VPP version was resolved to %q from VPP_VERSION file", ver)
130+
return ver
131+
}
132+
133+
// check env variable override
134+
if ver := os.Getenv(VPPVersionEnvVar); ver != "" {
135+
logrus.Debugf("VPP version was manually set to %q via %s env var", ver, VPPVersionEnvVar)
136+
return ver
137+
}
138+
125139
// assuming VPP package is installed
126-
if _, err := exec.LookPath("vpp"); err == nil {
140+
if filepath.Clean(apidir) == DefaultDir {
127141
version, err := GetVPPVersionInstalled()
128142
if err != nil {
129143
logrus.Debugf("ERR: failed to resolve VPP version from installed package: %v", err)

cmd/govpp/cli.go

+108
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,111 @@
1313
// limitations under the License.
1414

1515
package main
16+
17+
import (
18+
"io"
19+
20+
"github.com/docker/cli/cli/streams"
21+
"github.com/moby/term"
22+
)
23+
24+
type Cli interface {
25+
Out() *streams.Out
26+
Err() io.Writer
27+
In() *streams.In
28+
Apply(...CliOption) error
29+
}
30+
31+
type AppCli struct {
32+
out *streams.Out
33+
err io.Writer
34+
in *streams.In
35+
}
36+
37+
func NewCli(opt ...CliOption) (Cli, error) {
38+
cli := new(AppCli)
39+
if err := cli.Apply(opt...); err != nil {
40+
return nil, err
41+
}
42+
if cli.out == nil || cli.in == nil || cli.err == nil {
43+
stdin, stdout, stderr := term.StdStreams()
44+
if cli.in == nil {
45+
cli.in = streams.NewIn(stdin)
46+
}
47+
if cli.out == nil {
48+
cli.out = streams.NewOut(stdout)
49+
}
50+
if cli.err == nil {
51+
cli.err = stderr
52+
}
53+
}
54+
return cli, nil
55+
}
56+
57+
func (cli *AppCli) Out() *streams.Out {
58+
return cli.out
59+
}
60+
61+
func (cli *AppCli) Err() io.Writer {
62+
return cli.err
63+
}
64+
65+
func (cli *AppCli) In() *streams.In {
66+
return cli.in
67+
}
68+
69+
func (cli *AppCli) Apply(opt ...CliOption) error {
70+
for _, o := range opt {
71+
if err := o(cli); err != nil {
72+
return err
73+
}
74+
}
75+
return nil
76+
}
77+
78+
type CliOption func(cli *AppCli) error
79+
80+
// WithStandardStreams sets a cli in, out and err streams with the standard streams.
81+
func WithStandardStreams() CliOption {
82+
return func(cli *AppCli) error {
83+
// Set terminal emulation based on platform as required.
84+
stdin, stdout, stderr := term.StdStreams()
85+
cli.in = streams.NewIn(stdin)
86+
cli.out = streams.NewOut(stdout)
87+
cli.err = stderr
88+
return nil
89+
}
90+
}
91+
92+
// WithCombinedStreams uses the same stream for the output and error streams.
93+
func WithCombinedStreams(combined io.Writer) CliOption {
94+
return func(cli *AppCli) error {
95+
cli.out = streams.NewOut(combined)
96+
cli.err = combined
97+
return nil
98+
}
99+
}
100+
101+
// WithInputStream sets a cli input stream.
102+
func WithInputStream(in io.ReadCloser) CliOption {
103+
return func(cli *AppCli) error {
104+
cli.in = streams.NewIn(in)
105+
return nil
106+
}
107+
}
108+
109+
// WithOutputStream sets a cli output stream.
110+
func WithOutputStream(out io.Writer) CliOption {
111+
return func(cli *AppCli) error {
112+
cli.out = streams.NewOut(out)
113+
return nil
114+
}
115+
}
116+
117+
// WithErrorStream sets a cli error stream.
118+
func WithErrorStream(err io.Writer) CliOption {
119+
return func(cli *AppCli) error {
120+
cli.err = err
121+
return nil
122+
}
123+
}

cmd/govpp/cmd.go

+13-9
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,32 @@ const logo = `
3131
`
3232

3333
func Execute() {
34-
root := newRootCmd()
34+
cli, err := NewCli()
35+
if err != nil {
36+
logrus.Fatalf("CLI init error: %v", err)
37+
}
38+
root := newRootCmd(cli)
3539

3640
if err := root.Execute(); err != nil {
3741
logrus.Fatalf("ERROR: %v", err)
3842
}
3943
}
4044

41-
func newRootCmd() *cobra.Command {
45+
func newRootCmd(cli Cli) *cobra.Command {
4246
var (
4347
glob GlobalOptions
4448
)
4549
cmd := &cobra.Command{
46-
Use: "govpp [options] command",
47-
Short: "GoVPP CLI app",
50+
Use: "govpp [OPTIONS] COMMAND",
51+
Short: "GoVPP CLI",
4852
Long: color.Sprintf(logo, version.Short(), version.BuiltBy(), version.BuildTime()),
4953
Version: version.String(),
5054
SilenceUsage: true,
5155
SilenceErrors: true,
5256
TraverseChildren: true,
5357
CompletionOptions: cobra.CompletionOptions{HiddenDefaultCmd: true},
5458
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
55-
InitOptions(&glob)
59+
InitOptions(cli, &glob)
5660
logrus.Tracef("global options: %+v", glob)
5761

5862
logrus.Tracef("args: %+v", args)
@@ -68,10 +72,10 @@ func newRootCmd() *cobra.Command {
6872
glob.InstallFlags(cmd.PersistentFlags())
6973

7074
cmd.AddCommand(
71-
newGenerateCmd(),
72-
newVppapiCmd(),
73-
newHttpCmd(),
74-
newCliCommand(),
75+
newGenerateCmd(cli),
76+
newVppapiCmd(cli),
77+
newHttpCmd(cli),
78+
newCliCommand(cli),
7579
)
7680

7781
cmd.InitDefaultVersionFlag()

cmd/govpp/cmd_cli.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type CliOptions struct {
5959
Stdout io.Writer
6060
}
6161

62-
func newCliCommand() *cobra.Command {
62+
func newCliCommand(Cli) *cobra.Command {
6363
var (
6464
opts = CliOptions{
6565
ApiSocket: socketclient.DefaultSocketName,

cmd/govpp/cmd_generate.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type GenerateOptions struct {
3434
RunPlugins []string
3535
}
3636

37-
func newGenerateCmd() *cobra.Command {
37+
func newGenerateCmd(cli Cli) *cobra.Command {
3838
var (
3939
opts = GenerateOptions{
4040
RunPlugins: []string{"rpc"},
@@ -44,6 +44,7 @@ func newGenerateCmd() *cobra.Command {
4444
Use: "generate [apifile...]",
4545
Aliases: []string{"gen"},
4646
Short: "Generate code",
47+
Long: "Generates Go bindings for VPP API",
4748
RunE: func(cmd *cobra.Command, args []string) error {
4849
if opts.Input == "" {
4950
opts.Input = detectVppApiInput()

0 commit comments

Comments
 (0)