Skip to content

Commit 325396e

Browse files
authored
Add Plugins Support
1 parent 74556e0 commit 325396e

File tree

13 files changed

+703
-23
lines changed

13 files changed

+703
-23
lines changed

artifactory/utils/search_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package utils
22

33
import (
44
"bytes"
5-
"github.com/jfrog/jfrog-cli-core/utils/coreutils"
5+
corelog "github.com/jfrog/jfrog-cli-core/utils/log"
66
"os"
77
"path/filepath"
88
"testing"
@@ -19,7 +19,7 @@ func TestPrintSearchResults(t *testing.T) {
1919
reader := content.NewContentReader(filepath.Join(testdataPath, "search_results.json"), content.DefaultKey)
2020

2121
previousLog := log.Logger
22-
newLog := log.NewLogger(coreutils.GetCliLogLevel(), nil)
22+
newLog := log.NewLogger(corelog.GetCliLogLevel(), nil)
2323
// Restore previous logger when the function returns.
2424
defer log.SetLogger(previousLog)
2525

docs/common/helputils.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package common
2+
3+
import (
4+
"fmt"
5+
"github.com/codegangsta/cli"
6+
"strings"
7+
)
8+
9+
func CreateUsage(command string, name string, commands []string) string {
10+
return "\nName:\n\t" + "jfrog " + command + " - " + name + "\n\nUsage:\n\t" + strings.Join(commands[:], "\n\t") + "\n"
11+
}
12+
13+
func CreateBashCompletionFunc(extraCommands ...string) cli.BashCompleteFunc {
14+
return func(ctx *cli.Context) {
15+
for _, command := range extraCommands {
16+
fmt.Println(command)
17+
}
18+
flagNames := append(ctx.FlagNames(), "help")
19+
for _, flagName := range flagNames {
20+
fmt.Println("--" + flagName)
21+
}
22+
}
23+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ require (
2626

2727
replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go dev
2828

29-
replace github.com/jfrog/gocmd => github.com/jfrog/gocmd master
29+
replace github.com/jfrog/gocmd => github.com/jfrog/gocmd master

go.sum

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
3434
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
3535
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
3636
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
37-
github.com/jfrog/gocmd v0.1.15 h1:ZPticRK4gM0V9vCpWFAB4MaZq1OndvfYdeJD7tMZnn8=
38-
github.com/jfrog/gocmd v0.1.15/go.mod h1:yknNw8ubW0y8lIZIZlpX0/tQvxocDPV262O31rPcpsM=
37+
github.com/jfrog/gocmd v0.1.16-0.20200915092507-5004d59f2c22 h1:FZ5Equfr/pxrEQRIqMoyxFAtHytmcr6atIPOPfu0hVY=
38+
github.com/jfrog/gocmd v0.1.16-0.20200915092507-5004d59f2c22/go.mod h1:yknNw8ubW0y8lIZIZlpX0/tQvxocDPV262O31rPcpsM=
3939
github.com/jfrog/gofrog v1.0.6 h1:yUDxSCw8gTK6vC4PvtG0HTnEOQJSZ+O4lWGCgkev1nU=
4040
github.com/jfrog/gofrog v1.0.6/go.mod h1:HkDzg+tMNw23UryoOv0+LB94BzYcl6MCIoz8Tmlb+s8=
41-
github.com/jfrog/jfrog-client-go v0.13.3-0.20200907115924-c7dacd8219de h1:uoI8HSGlT5Dw724hNAH+vCzNqc6d/ZcMUrXSHCN3T9c=
42-
github.com/jfrog/jfrog-client-go v0.13.3-0.20200907115924-c7dacd8219de/go.mod h1:Av1I4oFHu+frbD+xqZV4fdAp3Kpt43uFfBnV6Hag+t8=
41+
github.com/jfrog/jfrog-client-go v0.13.4-0.20200915135300-0bddc58cb170 h1:9TgGoK8Bf3PfRqItE195bYa2c+aZuRY9vQBoxhbV6qY=
42+
github.com/jfrog/jfrog-client-go v0.13.4-0.20200915135300-0bddc58cb170/go.mod h1:yjBR3LThf5UXnXWT75QpYJzy+Op7mlwDdK0ETBk1q0w=
4343
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
4444
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
4545
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@@ -106,6 +106,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
106106
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
107107
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
108108
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
109+
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
110+
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
109111
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
110112
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
111113
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -114,7 +116,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB
114116
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
115117
golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4=
116118
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
117-
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
118119
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
119120
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
120121
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=

plugins/components/commandcomp.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package components
2+
3+
type Argument struct {
4+
Name string
5+
Description string
6+
}
7+
8+
type EnvVar struct {
9+
Name string
10+
Default string
11+
Description string
12+
}
13+
14+
type ActionFunc func(c *Context) error
15+
16+
type Context struct {
17+
Arguments []string
18+
stringFlags map[string]string
19+
boolFlags map[string]bool
20+
}
21+
22+
func (c *Context) GetStringFlagValue(flagName string) string {
23+
return c.stringFlags[flagName]
24+
}
25+
26+
func (c *Context) GetBoolFlagValue(flagName string) bool {
27+
return c.boolFlags[flagName]
28+
}
29+
30+
type Flag interface {
31+
GetName() string
32+
GetDescription() string
33+
}
34+
35+
type StringFlag struct {
36+
Name string
37+
Description string
38+
// A flag with default value cannot be mandatory.
39+
DefaultValue string
40+
Mandatory bool
41+
}
42+
43+
func (f StringFlag) GetName() string {
44+
return f.Name
45+
}
46+
47+
func (f StringFlag) GetDescription() string {
48+
return f.Description
49+
}
50+
51+
func (f StringFlag) GetDefault() string {
52+
return f.DefaultValue
53+
}
54+
55+
func (f StringFlag) isMandatory() bool {
56+
return f.Mandatory
57+
}
58+
59+
type BoolFlag struct {
60+
Name string
61+
Description string
62+
DefaultValue bool
63+
}
64+
65+
func (f BoolFlag) GetName() string {
66+
return f.Name
67+
}
68+
69+
func (f BoolFlag) GetDescription() string {
70+
return f.Description
71+
}
72+
73+
func (f BoolFlag) GetDefault() bool {
74+
return f.DefaultValue
75+
}
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
package components
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"github.com/codegangsta/cli"
7+
"github.com/jfrog/jfrog-cli-core/docs/common"
8+
"strings"
9+
)
10+
11+
func ConvertApp(jfrogApp App) (*cli.App, error) {
12+
var err error
13+
app := cli.NewApp()
14+
app.Name = jfrogApp.Name
15+
app.Usage = jfrogApp.Description
16+
app.Version = jfrogApp.Version
17+
app.Commands, err = convertCommands(jfrogApp)
18+
if err != nil {
19+
return nil, err
20+
}
21+
// Defaults:
22+
app.EnableBashCompletion = true
23+
return app, nil
24+
}
25+
26+
func convertCommands(jfrogApp App) ([]cli.Command, error) {
27+
var converted []cli.Command
28+
for _, cmd := range jfrogApp.Commands {
29+
cur, err := convertCommand(cmd, jfrogApp.Name)
30+
if err != nil {
31+
return converted, err
32+
}
33+
converted = append(converted, cur)
34+
}
35+
return converted, nil
36+
}
37+
38+
func convertCommand(cmd Command, appName string) (cli.Command, error) {
39+
convertedFlags, err := convertFlags(cmd)
40+
if err != nil {
41+
return cli.Command{}, err
42+
}
43+
return cli.Command{
44+
Name: cmd.Name,
45+
Flags: convertedFlags,
46+
Aliases: cmd.Aliases,
47+
Usage: cmd.Description,
48+
HelpName: common.CreateUsage(appName+" "+cmd.Name, cmd.Description, []string{createCommandUsage(cmd, appName)}),
49+
UsageText: createArgumentsSummary(cmd),
50+
ArgsUsage: createEnvVarsSummary(cmd),
51+
BashComplete: common.CreateBashCompletionFunc(),
52+
// Passing any other interface than 'cli.ActionFunc' will fail the command.
53+
Action: getActionFunc(cmd),
54+
}, nil
55+
}
56+
57+
func createCommandUsage(cmd Command, appName string) string {
58+
usage := fmt.Sprintf("jfrog %s %s", appName, cmd.Name)
59+
if len(cmd.Flags) > 0 {
60+
usage += " [command options]"
61+
}
62+
for _, argument := range cmd.Arguments {
63+
usage += fmt.Sprintf(" <%s>", argument.Name)
64+
}
65+
return usage
66+
}
67+
68+
func createArgumentsSummary(cmd Command) string {
69+
summary := ""
70+
for i, argument := range cmd.Arguments {
71+
if i > 0 {
72+
summary += "\n"
73+
}
74+
summary += "\t" + argument.Name + "\n\t\t" + argument.Description + "\n"
75+
}
76+
return summary
77+
}
78+
79+
func createEnvVarsSummary(cmd Command) string {
80+
var envVarsSummary []string
81+
for i, env := range cmd.EnvVars {
82+
summary := ""
83+
if i > 0 {
84+
summary += "\n"
85+
}
86+
summary = "\t" + env.Name + "\n"
87+
if env.Default != "" {
88+
summary += "\t\t[Default: " + env.Default + "]\n"
89+
}
90+
summary += "\t\t" + env.Description
91+
envVarsSummary = append(envVarsSummary, summary)
92+
}
93+
return strings.Join(envVarsSummary[:], "\n\n")
94+
}
95+
96+
func convertFlags(cmd Command) ([]cli.Flag, error) {
97+
var convertedFlags []cli.Flag
98+
for _, flag := range cmd.Flags {
99+
converted, err := convertByType(flag)
100+
if err != nil {
101+
return convertedFlags, err
102+
}
103+
if converted != nil {
104+
convertedFlags = append(convertedFlags, converted)
105+
}
106+
}
107+
return convertedFlags, nil
108+
}
109+
110+
func convertByType(flag Flag) (cli.Flag, error) {
111+
if f, ok := flag.(StringFlag); ok {
112+
return convertStringFlag(f), nil
113+
}
114+
if f, ok := flag.(BoolFlag); ok {
115+
return convertBoolFlag(f), nil
116+
}
117+
return nil, errors.New(fmt.Sprintf("Flag '%s' does not match any known flag type.", flag.GetName()))
118+
}
119+
120+
func convertStringFlag(f StringFlag) cli.Flag {
121+
stringFlag := cli.StringFlag{
122+
Name: f.Name,
123+
Usage: f.Description + "` `",
124+
}
125+
// If default is set, add its value and return.
126+
if f.DefaultValue != "" {
127+
stringFlag.Usage = fmt.Sprintf("[Default: %s] %s", f.DefaultValue, stringFlag.Usage)
128+
return stringFlag
129+
}
130+
// Otherwise, mark as mandatory/optional accordingly.
131+
if f.Mandatory {
132+
stringFlag.Usage = "[Mandatory] " + stringFlag.Usage
133+
} else {
134+
stringFlag.Usage = "[Optional] " + stringFlag.Usage
135+
}
136+
return stringFlag
137+
}
138+
139+
func convertBoolFlag(f BoolFlag) cli.Flag {
140+
if f.DefaultValue {
141+
return cli.BoolTFlag{
142+
Name: f.Name,
143+
Usage: "[Default: true] " + f.Description + "` `",
144+
}
145+
}
146+
return cli.BoolFlag{
147+
Name: f.Name,
148+
Usage: "[Default: false] " + f.Description + "` `",
149+
}
150+
}
151+
152+
// Wrap the base's ActionFunc with our own, while retrieving needed information from the Context.
153+
func getActionFunc(cmd Command) cli.ActionFunc {
154+
return func(baseContext *cli.Context) error {
155+
pluginContext := &Context{}
156+
pluginContext.Arguments = baseContext.Args()
157+
err := fillFlagMaps(pluginContext, baseContext, cmd.Flags)
158+
if err != nil {
159+
return err
160+
}
161+
return cmd.Action(pluginContext)
162+
}
163+
}
164+
165+
func fillFlagMaps(c *Context, baseContext *cli.Context, originalFlags []Flag) error {
166+
c.stringFlags = make(map[string]string)
167+
c.boolFlags = make(map[string]bool)
168+
169+
// Loop over all plugin's known flags.
170+
for _, flag := range originalFlags {
171+
if stringFlag, ok := flag.(StringFlag); ok {
172+
finalValue, err := getValueForStringFlag(stringFlag, baseContext.String(stringFlag.Name))
173+
if err != nil {
174+
return err
175+
}
176+
c.stringFlags[stringFlag.Name] = finalValue
177+
continue
178+
}
179+
180+
if boolFlag, ok := flag.(BoolFlag); ok {
181+
c.boolFlags[boolFlag.Name] = getValueForBoolFlag(boolFlag, baseContext)
182+
}
183+
}
184+
return nil
185+
}
186+
187+
func getValueForStringFlag(f StringFlag, receivedValue string) (finalValue string, err error) {
188+
if receivedValue != "" {
189+
return receivedValue, nil
190+
}
191+
// Empty but has a default value defined.
192+
if f.DefaultValue != "" {
193+
return f.DefaultValue, nil
194+
}
195+
// Empty but mandatory.
196+
if f.Mandatory {
197+
return "", errors.New("Mandatory flag '" + f.Name + "' is missing")
198+
}
199+
return "", nil
200+
}
201+
202+
func getValueForBoolFlag(f BoolFlag, baseContext *cli.Context) bool {
203+
if f.DefaultValue {
204+
return baseContext.BoolT(f.Name)
205+
}
206+
return baseContext.Bool(f.Name)
207+
}

0 commit comments

Comments
 (0)