未验证 提交 818ba092 编写于 作者: LinuxSuRen's avatar LinuxSuRen 提交者: GitHub

Add test cases for interactive commands (#297)

* Add test cases for message confirm

* Add more test cases for the confirming case

* Add more test cases

* Add test cases for restart Jenkins command

* Add more test cases

* Add more test cases

* Add more test cases

* Fix the build test error

* Add test cases for user edit

* Add test cases for config edit command

* Add more test cases for config generate and remove cmd

* Remove unused interface

* Do not test copy

* Add test cases for config add command

* Add test cases for config select command

* Add test cases for open jenkins command

* Fix missing of stdio

* Fix the error of config remove test
上级 44ca7d17
......@@ -76,7 +76,7 @@ fmt:
go fmt ./client/...
go fmt ./app/...
test: gen-data verify fmt
test: clean gen-data verify fmt
mkdir -p bin
go vet ./...
go test ./... -v -coverprofile coverage.out
......
......@@ -3,9 +3,11 @@ package cmd
import (
"encoding/json"
"fmt"
"github.com/AlecAivazis/survey/v2/terminal"
"gopkg.in/yaml.v2"
"io"
"net/http"
"os"
"reflect"
"strings"
......@@ -28,6 +30,8 @@ type CommonOption struct {
SystemCallExec util.SystemCallExec
LookPathContext util.LookPathContext
RoundTripper http.RoundTripper
Stdio terminal.Stdio
}
// OutputOption represent the format of output
......@@ -188,24 +192,74 @@ func (o *OutputOption) SetFlagWithHeaders(cmd *cobra.Command, headers string) {
// BatchOption represent the options for a batch operation
type BatchOption struct {
Batch bool
Stdio terminal.Stdio
}
// MsgConfirm is the interface for confirming a message
type MsgConfirm interface {
Confirm(message string) bool
}
// Confirm promote user if they really want to do this
func (b *BatchOption) Confirm(message string) bool {
if !b.Batch {
confirm := false
prompt := &survey.Confirm{
var prompt survey.Prompt
prompt = &survey.Confirm{
Message: message,
}
survey.AskOne(prompt, &confirm)
if !confirm {
return false
}
survey.AskOne(prompt, &confirm, survey.WithStdio(b.Stdio.In, b.Stdio.Out, b.Stdio.Err))
return confirm
}
return true
}
// GetSystemStdio returns the stdio from system
func GetSystemStdio() terminal.Stdio {
return terminal.Stdio{
In: os.Stdin,
Out: os.Stdout,
Err: os.Stderr,
}
}
// EditContent is the interface for editing content from a file
type EditContent interface {
Editor(defaultContent, message string) (content string, err error)
}
// Selector is the interface for selecting an option
type Selector interface {
Select(options []string, message, defaultOpt string) (target string, err error)
}
// Editor edit a file than return the content
func (o *CommonOption) Editor(defaultContent, message string) (content string, err error) {
prompt := &survey.Editor{
Message: message,
FileName: "*.sh",
Default: defaultContent,
HideDefault: true,
AppendDefault: true,
}
err = survey.AskOne(prompt, &content, survey.WithStdio(o.Stdio.In, o.Stdio.Out, o.Stdio.Err))
return
}
// Select return a target
func (o *CommonOption) Select(options []string, message, defaultOpt string) (target string, err error) {
prompt := &survey.Select{
Message: message,
Options: options,
Default: defaultOpt,
}
err = survey.AskOne(prompt, &target, survey.WithStdio(o.Stdio.In, o.Stdio.Out, o.Stdio.Err))
return
}
// SetFlag the flag for batch option
func (b *BatchOption) SetFlag(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&b.Batch, "batch", "b", false, "Batch mode, no need confirm")
......
......@@ -225,12 +225,3 @@ func saveConfig() (err error) {
}
return
}
// GetConfigFromHome returns the config file path from user home dir
func GetConfigFromHome() (configPath string, homeErr error) {
userHome, homeErr := homedir.Dir()
if homeErr == nil {
configPath = fmt.Sprintf("%s/.jenkins-cli.yaml", userHome)
}
return
}
......@@ -2,8 +2,6 @@ package cmd
import (
"fmt"
"log"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/spf13/cobra"
......@@ -38,10 +36,8 @@ var configAddCmd = &cobra.Command{
Use: "add",
Short: i18n.T("Add a Jenkins config item"),
Long: i18n.T("Add a Jenkins config item"),
Run: func(_ *cobra.Command, _ []string) {
if err := addJenkins(configAddOptions.JenkinsServer); err != nil {
log.Fatal(err)
}
RunE: func(_ *cobra.Command, _ []string) error {
return addJenkins(configAddOptions.JenkinsServer)
},
Example: "jcli config add -n demo",
}
......@@ -49,12 +45,12 @@ var configAddCmd = &cobra.Command{
func addJenkins(jenkinsServer JenkinsServer) (err error) {
jenkinsName := jenkinsServer.Name
if jenkinsName == "" {
err = fmt.Errorf("Name cannot be empty")
err = fmt.Errorf("name cannot be empty")
return
}
if findJenkinsByName(jenkinsName) != nil {
err = fmt.Errorf("Jenkins %s is existed", jenkinsName)
err = fmt.Errorf("jenkins %s is existed", jenkinsName)
return
}
......
package cmd
import (
"bytes"
"io"
"io/ioutil"
"os"
"path"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("config add command", func() {
var (
ctrl *gomock.Controller
buf io.Writer
err error
configPath string
)
BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
rootCmd.SetArgs([]string{})
buf = new(bytes.Buffer)
rootCmd.SetOutput(buf)
rootOptions.Jenkins = ""
configPath = path.Join(os.TempDir(), "fake.yaml")
var data []byte
data, err = generateSampleConfig()
Expect(err).To(BeNil())
err = ioutil.WriteFile(configPath, data, 0664)
Expect(err).To(BeNil())
})
AfterEach(func() {
rootCmd.SetArgs([]string{})
os.Remove(configPath)
rootOptions.ConfigFile = ""
ctrl.Finish()
})
Context("basic cases", func() {
It("lack of name", func() {
rootCmd.SetArgs([]string{"config", "add", "--configFile", configPath})
_, err = rootCmd.ExecuteC()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("name cannot be empty"))
})
It("add an exist one", func() {
rootCmd.SetArgs([]string{"config", "add", "--name", "yourServer", "--configFile", configPath})
_, err = rootCmd.ExecuteC()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("jenkins yourServer is existed"))
})
It("should success", func() {
rootCmd.SetArgs([]string{"config", "add", "--name", "fake", "--configFile", configPath})
_, err = rootCmd.ExecuteC()
Expect(err).NotTo(HaveOccurred())
})
})
})
......@@ -2,9 +2,7 @@ package cmd
import (
"fmt"
"log"
"github.com/AlecAivazis/survey/v2"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"io/ioutil"
......@@ -12,40 +10,35 @@ import (
"github.com/spf13/cobra"
)
// ConfigEditOption is the option for edit config command
type ConfigEditOption struct {
CommonOption
}
var configEditOption ConfigEditOption
func init() {
configCmd.AddCommand(configEditCmd)
configEditOption.Stdio = GetSystemStdio()
}
var configEditCmd = &cobra.Command{
Use: "edit",
Short: i18n.T("Edit a Jenkins config"),
Long: i18n.T(`Edit a Jenkins config`),
Run: func(_ *cobra.Command, _ []string) {
current := getCurrentJenkinsFromOptionsOrDie()
RunE: func(_ *cobra.Command, _ []string) (err error) {
current := getCurrentJenkinsFromOptions()
configPath := configOptions.ConfigFileLocation
var data []byte
var err error
if data, err = ioutil.ReadFile(configPath); err != nil {
log.Fatal(err)
}
content := string(data)
prompt := &survey.Editor{
Message: fmt.Sprintf("Edit config item %s", current.Name),
FileName: "*.yaml",
Help: fmt.Sprintf("Config file path: %s", configPath),
Default: content,
HideDefault: true,
AppendDefault: true,
}
if err := survey.AskOne(prompt, &content); err == nil {
if err = ioutil.WriteFile(configPath, []byte(content), 0644); err != nil {
log.Fatal(err)
if data, err = ioutil.ReadFile(configPath); err == nil {
content := string(data)
//Help: fmt.Sprintf("Config file path: %s", configPath),
content, err = configEditOption.Editor(content, fmt.Sprintf("Edit config item %s", current.Name))
if err == nil {
err = ioutil.WriteFile(configPath, []byte(content), 0644)
}
} else {
log.Fatal(err)
}
return
},
}
package cmd
import (
"github.com/Netflix/go-expect"
"io/ioutil"
"testing"
"time"
"github.com/AlecAivazis/survey/v2/terminal"
)
func TestEditConfig(t *testing.T) {
RunEditCommandTest(t, EditCommandTest{
Procedure: func(c *expect.Console) {
c.ExpectString("Edit config item yourServer")
c.SendLine("")
go c.ExpectEOF()
time.Sleep(time.Millisecond)
c.Send("\x1b")
c.SendLine(":wq!")
},
Test: func(stdio terminal.Stdio) (err error) {
rootOptions.ConfigFile = "test.yaml"
data, err := generateSampleConfig()
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
rootCmd.SetArgs([]string{"config", "edit"})
configEditOption.CommonOption.Stdio = stdio
_, err = rootCmd.ExecuteC()
return
},
})
}
package cmd
import (
"fmt"
"github.com/mitchellh/go-homedir"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"github.com/AlecAivazis/survey/v2"
"github.com/atotto/clipboard"
"github.com/jenkins-zh/jenkins-cli/app/helper"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
)
// ConfigGenerateOption is the config generate cmd option
type ConfigGenerateOption struct {
InteractiveOption
CommonOption
BatchOption
Copy bool
}
......@@ -26,6 +29,8 @@ func init() {
i18n.T("Interactive mode"))
configGenerateCmd.Flags().BoolVarP(&configGenerateOption.Copy, "copy", "c", false,
i18n.T("Copy the output into clipboard"))
configGenerateOption.CommonOption.Stdio = GetSystemStdio()
configGenerateOption.BatchOption.Stdio = GetSystemStdio()
}
var configGenerateCmd = &cobra.Command{
......@@ -33,11 +38,12 @@ var configGenerateCmd = &cobra.Command{
Aliases: []string{"gen"},
Short: i18n.T("Generate a sample config file for you"),
Long: i18n.T("Generate a sample config file for you"),
Run: func(cmd *cobra.Command, _ []string) {
data, err := generateSampleConfig()
RunE: func(cmd *cobra.Command, _ []string) (err error) {
var data []byte
data, err = generateSampleConfig()
if err == nil {
if configGenerateOption.Interactive {
err = InteractiveWithConfig(cmd, data)
err = configGenerateOption.InteractiveWithConfig(cmd, data)
} else {
printCfg(cmd, data)
}
......@@ -46,39 +52,21 @@ var configGenerateCmd = &cobra.Command{
err = clipboard.WriteAll(string(data))
}
}
helper.CheckErr(cmd, err)
return
},
}
// InteractiveWithConfig be friendly for a newer
func InteractiveWithConfig(cmd *cobra.Command, data []byte) (err error) {
func (o *ConfigGenerateOption) InteractiveWithConfig(cmd *cobra.Command, data []byte) (err error) {
configPath := configOptions.ConfigFileLocation
if configPath == "" { // config file isn't exists
if configPath, err = GetConfigFromHome(); err != nil {
return
}
}
_, err = os.Stat(configPath)
if err != nil && os.IsNotExist(err) {
confirm := false
prompt := &survey.Confirm{
Message: "Cannot found your config file, do you want to edit it?",
}
err = survey.AskOne(prompt, &confirm)
if err == nil && confirm {
prompt := &survey.Editor{
Message: "Edit your config file",
FileName: "*.yaml",
Default: string(data),
HideDefault: true,
AppendDefault: true,
}
var configContext string
if err = survey.AskOne(prompt, &configContext); err == nil {
err = ioutil.WriteFile(configPath, []byte(configContext), 0644)
confirm := o.Confirm("Cannot found your config file, do you want to edit it?")
if confirm {
var content string
content, err = o.Editor(string(data), "Edit your config file")
if err == nil {
err = ioutil.WriteFile(configPath, []byte(content), 0644)
}
}
}
......@@ -129,3 +117,12 @@ func generateSampleConfig() ([]byte, error) {
sampleConfig := getSampleConfig()
return yaml.Marshal(&sampleConfig)
}
// GetConfigFromHome returns the config file path from user home dir
func GetConfigFromHome() (configPath string, homeErr error) {
userHome, homeErr := homedir.Dir()
if homeErr == nil {
configPath = fmt.Sprintf("%s/.jenkins-cli.yaml", userHome)
}
return
}
......@@ -2,9 +2,15 @@ package cmd
import (
"bytes"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/Netflix/go-expect"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"os"
"path"
"testing"
"time"
)
var _ = Describe("config generate command", func() {
......@@ -65,3 +71,31 @@ mirrors:
})
})
})
func TestConfigGenerate(t *testing.T) {
RunEditCommandTest(t, EditCommandTest{
ConfirmProcedure: func(c *expect.Console) {
c.ExpectString("Cannot found your config file, do you want to edit it?")
c.SendLine("y")
//c.ExpectEOF()
},
Procedure: func(c *expect.Console) {
c.ExpectString("Edit your config file")
c.SendLine("")
go c.ExpectEOF()
time.Sleep(time.Millisecond)
c.Send(`ifake-config`)
c.Send("\x1b")
c.SendLine(":wq!")
},
Test: func(stdio terminal.Stdio) (err error) {
configFile := path.Join(os.TempDir(), "fake.yaml")
defer os.Remove(configFile)
configGenerateOption.BatchOption.Stdio = stdio
configGenerateOption.CommonOption.Stdio = stdio
rootCmd.SetArgs([]string{"config", "generate", "--interactive", "--copy=false", "--configFile=" + configFile})
_, err = rootCmd.ExecuteC()
return
},
})
}
......@@ -12,7 +12,7 @@ func init() {
}
var configRemoveCmd = &cobra.Command{
Use: "remove <name>",
Use: "remove",
Short: i18n.T("Remove a Jenkins config"),
Long: i18n.T("Remove a Jenkins config"),
Args: cobra.MinimumNArgs(1),
......@@ -25,7 +25,7 @@ var configRemoveCmd = &cobra.Command{
func removeJenkins(name string) (err error) {
current := getCurrentJenkins()
if name == current.Name {
err = fmt.Errorf("You cannot remove current Jenkins, if you want to remove it, can select other items before")
err = fmt.Errorf("you cannot remove current Jenkins, if you want to remove it, can select other items before")
return
}
......@@ -39,7 +39,7 @@ func removeJenkins(name string) (err error) {
}
if index == -1 {
err = fmt.Errorf("Cannot found by name %s", name)
err = fmt.Errorf("cannot found by name %s", name)
} else {
config.JenkinsServers = append(config.JenkinsServers[:index], config.JenkinsServers[index+1:]...)
err = saveConfig()
......
package cmd
import (
"bytes"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"path"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("config remove command", func() {
var (
ctrl *gomock.Controller
buf *bytes.Buffer
configPath string
err error
otherJenkins string
)
BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
rootCmd.SetArgs([]string{})
buf = new(bytes.Buffer)
rootCmd.SetOutput(buf)
rootOptions.Jenkins = ""
configPath = path.Join(os.TempDir(), "fake.yaml")
otherJenkins = "other-jenkins"
var data []byte
sampleConfig := getSampleConfig()
sampleConfig.JenkinsServers = append(sampleConfig.JenkinsServers, JenkinsServer{
Name: otherJenkins,
})
data, err = yaml.Marshal(&sampleConfig)
Expect(err).To(BeNil())
err = ioutil.WriteFile(configPath, data, 0664)
Expect(err).To(BeNil())
})
AfterEach(func() {
rootCmd.SetArgs([]string{})
os.Remove(configPath)
ctrl.Finish()
})
Context("basic cases", func() {
var (
err error
)
It("remove a not exist jenkins", func() {
rootCmd.SetArgs([]string{"config", "remove", "fake", "--configFile", configPath})
_, err = rootCmd.ExecuteC()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("cannot found by name fake"))
})
It("remove the current jenkins", func() {
rootCmd.SetArgs([]string{"config", "remove", "yourServer", "--configFile", configPath})
_, err = rootCmd.ExecuteC()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("you cannot remove current Jenkins, if you want to remove it, can select other items before"))
})
It("should success", func() {
rootCmd.SetArgs([]string{"config", "remove", otherJenkins, "--configFile", configPath})
_, err = rootCmd.ExecuteC()
Expect(err).NotTo(HaveOccurred())
})
})
})
package cmd
import (
"github.com/AlecAivazis/survey/v2"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/spf13/cobra"
)
// ConfigSelectOptions is the option for select a config
type ConfigSelectOptions struct {
CommonOption
}
func init() {
configCmd.AddCommand(configSelectCmd)
configSelectOptions.Stdio = GetSystemStdio()
}
var configSelectOptions ConfigSelectOptions
var configSelectCmd = &cobra.Command{
Use: "select [<name>]",
Use: "select",
Short: i18n.T("Select one config as current Jenkins"),
Long: i18n.T("Select one config as current Jenkins"),
Run: func(_ *cobra.Command, args []string) {
RunE: func(_ *cobra.Command, args []string) (err error) {
var jenkinsName string
if len(args) > 0 {
jenkinsName := args[0]
jenkinsName = args[0]
} else {
target := ""
if currentJenkins := getCurrentJenkins(); currentJenkins != nil {
target = currentJenkins.Name
}
jenkinsName, err = configSelectOptions.Select(getJenkinsNames(),
"Choose a Jenkins as the current one:", target)
}
if err == nil {
setCurrentJenkins(jenkinsName)
} else {
selectByManual()
}
return
},
}
func selectByManual() {
target := ""
if currentJenkins := getCurrentJenkins(); currentJenkins != nil {
target = currentJenkins.Name
}
prompt := &survey.Select{
Message: "Choose a Jenkins as the current one:",
Options: getJenkinsNames(),
Default: target,
}
if err := survey.AskOne(prompt, &target); err == nil && target != "" {
setCurrentJenkins(target)
}
}
package cmd
import (
"bytes"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/Netflix/go-expect"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"path"
"testing"
)
var _ = Describe("config select command", func() {
var (
ctrl *gomock.Controller
buf *bytes.Buffer
configPath string
err error
)
BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
rootCmd.SetArgs([]string{})
buf = new(bytes.Buffer)
rootCmd.SetOutput(buf)
rootOptions.Jenkins = ""
configPath = path.Join(os.TempDir(), "fake.yaml")
var data []byte
sampleConfig := getSampleConfig()
data, err = yaml.Marshal(&sampleConfig)
Expect(err).To(BeNil())
err = ioutil.WriteFile(configPath, data, 0664)
Expect(err).To(BeNil())
})
AfterEach(func() {
rootCmd.SetArgs([]string{})
os.Remove(configPath)
ctrl.Finish()
})
Context("basic cases", func() {
var (
err error
)
It("select a config", func() {
rootCmd.SetArgs([]string{"config", "select", "yourServer", "fake", "--configFile", configPath})
_, err = rootCmd.ExecuteC()
Expect(err).NotTo(HaveOccurred())
})
})
})
func TestConfigSelect(t *testing.T) {
RunEditCommandTest(t, EditCommandTest{
ConfirmProcedure: func(c *expect.Console) {
c.ExpectString("Choose a Jenkins as the current one:")
// filter away everything
c.SendLine("z")
// send enter (should get ignored since there are no answers)
c.SendLine(string(terminal.KeyEnter))
// remove the filter we just applied
c.SendLine(string(terminal.KeyBackspace))
// press enter
c.SendLine(string(terminal.KeyEnter))
},
Test: func(stdio terminal.Stdio) (err error) {
configFile := path.Join(os.TempDir(), "fake.yaml")
defer os.Remove(configFile)
var data []byte
data, err = generateSampleConfig()
err = ioutil.WriteFile(configFile, data, 0664)
configSelectOptions.CommonOption.Stdio = stdio
rootCmd.SetArgs([]string{"config", "select", "--configFile", configFile})
_, err = rootCmd.ExecuteC()
return
},
})
}
......@@ -28,6 +28,7 @@ func init() {
credentialDeleteCmd.Flags().StringVarP(&credentialDeleteOption.ID, "id", "", "",
i18n.T("The ID of Jenkins credentials"))
credentialDeleteOption.SetFlag(credentialDeleteCmd)
credentialDeleteOption.Stdio = GetSystemStdio()
}
var credentialDeleteCmd = &cobra.Command{
......
......@@ -2,8 +2,11 @@ package cmd
import (
"bytes"
"github.com/Netflix/go-expect"
"io/ioutil"
"os"
"testing"
"time"
"github.com/jenkins-zh/jenkins-cli/client"
......@@ -59,9 +62,10 @@ var _ = Describe("credential delete command", func() {
})
It("lack of the necessary parameters", func() {
rootCmd.SetArgs([]string{"credential", "delete"})
rootCmd.SetArgs([]string{"credential", "delete", "--store=", "--id="})
_, err = rootCmd.ExecuteC()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("the store or id of target credential is empty"))
})
It("should success", func() {
......@@ -74,3 +78,91 @@ var _ = Describe("credential delete command", func() {
})
})
})
func TestConfirmCommands(t *testing.T) {
RunPromptCommandTest(t, PromptCommandTest{
Args: []string{"credential", "delete", "fake-store", "fake-id", "-b=false"},
Procedure: func(c *expect.Console) {
c.ExpectString("Are you sure to delete credential fake-id")
c.SendLine("n")
c.ExpectEOF()
},
BatchOption: &credentialDeleteOption.BatchOption,
})
RunPromptCommandTest(t, PromptCommandTest{
Args: []string{"job", "stop", "fake", "-b=false"},
Procedure: func(c *expect.Console) {
c.ExpectString("Are you sure to stop job fake ?")
c.SendLine("n")
c.ExpectEOF()
},
BatchOption: &jobStopOption.BatchOption,
})
RunPromptCommandTest(t, PromptCommandTest{
Args: []string{"job", "build", "fake", "-b=false"},
Procedure: func(c *expect.Console) {
c.ExpectString("Are you sure to build job fake")
c.SendLine("n")
c.ExpectEOF()
},
BatchOption: &jobBuildOption.BatchOption,
})
RunPromptCommandTest(t, PromptCommandTest{
Args: []string{"job", "delete", "fake", "-b=false"},
Procedure: func(c *expect.Console) {
c.ExpectString("Are you sure to delete job fake ?")
c.SendLine("n")
c.ExpectEOF()
},
BatchOption: &jobDeleteOption.BatchOption,
})
RunPromptCommandTest(t, PromptCommandTest{
Args: []string{"user", "delete", "fake-user", "-b=false"},
Procedure: func(c *expect.Console) {
c.ExpectString("Are you sure to delete user fake-user ?")
c.SendLine("n")
c.ExpectEOF()
},
BatchOption: &userDeleteOption.BatchOption,
})
RunPromptCommandTest(t, PromptCommandTest{
Args: []string{"restart", "-b=false"},
Procedure: func(c *expect.Console) {
c.ExpectString("Are you sure to restart Jenkins http://localhost:8080/jenkins?")
c.SendLine("n")
c.ExpectEOF()
},
BatchOption: &restartOption.BatchOption,
})
RunPromptTest(t, PromptTest{
Message: "message",
MsgConfirm: &BatchOption{},
Procedure: func(c *expect.Console) {
c.ExpectString("message")
c.SendLine("y")
c.ExpectEOF()
},
Expected: true,
})
RunEditorTest(t, EditorTest{
Message: "message",
DefaultContent: "hello",
EditContent: &CommonOption{},
Procedure: func(c *expect.Console) {
c.ExpectString("message")
c.SendLine("")
go c.ExpectEOF()
time.Sleep(time.Millisecond)
c.Send("ddigood\x1b")
c.SendLine(":wq!")
},
Expected: "good\n",
})
}
package cmd
import (
"bytes"
"fmt"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"net/http"
"strconv"
"github.com/jenkins-zh/jenkins-cli/app/helper"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/jenkins-zh/jenkins-cli/util"
"github.com/spf13/cobra"
)
// JobArtifactOption is the options of job artifact command
type JobArtifactOption struct {
OutputOption
RoundTripper http.RoundTripper
CommonOption
}
var jobArtifactOption JobArtifactOption
func init() {
jobCmd.AddCommand(jobArtifactCmd)
jobArtifactOption.SetFlag(jobArtifactCmd)
jobArtifactOption.SetFlagWithHeaders(jobArtifactCmd, "Name,Path,Size")
}
var jobArtifactCmd = &cobra.Command{
Use: "artifact <jobName> [buildID]",
Short: i18n.T("Print the artifact list of target job"),
Long: i18n.T("Print the artifact list of target job"),
Run: func(cmd *cobra.Command, args []string) {
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
argLen := len(args)
if argLen == 0 {
cmd.Help()
return
}
var err error
jobName := args[0]
buildID := -1
if argLen >= 2 {
if buildID, err = strconv.Atoi(args[1]); err != nil {
cmd.PrintErrln(err)
return
}
}
......@@ -57,32 +44,11 @@ var jobArtifactCmd = &cobra.Command{
}
getCurrentJenkinsAndClientOrDie(&(jclient.JenkinsCore))
artifacts, err := jclient.List(jobName, buildID)
if err == nil {
var data []byte
data, err = jobArtifactOption.Output(artifacts)
if err == nil {
cmd.Print(string(data))
}
var artifacts []client.Artifact
if artifacts, err = jclient.List(jobName, buildID); err == nil {
jobArtifactOption.Writer = cmd.OutOrStdout()
err = jobArtifactOption.OutputV2(artifacts)
}
helper.CheckErr(cmd, err)
return
},
}
// Output render data into byte array
func (o *JobArtifactOption) Output(obj interface{}) (data []byte, err error) {
if data, err = o.OutputOption.Output(obj); err != nil {
artifacts := obj.([]client.Artifact)
buf := new(bytes.Buffer)
table := util.CreateTable(buf)
table.AddRow("id", "name", "path", "size")
for _, artifact := range artifacts {
table.AddRow(artifact.ID, artifact.Name, artifact.Path, fmt.Sprintf("%d", artifact.Size))
}
table.Render()
err = nil
data = buf.Bytes()
}
return
}
......@@ -7,12 +7,10 @@ import (
"os"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/spf13/cobra"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/jenkins-zh/jenkins-cli/mock/mhttp"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("job artifact command", func() {
......@@ -47,14 +45,10 @@ var _ = Describe("job artifact command", func() {
buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)
jobArtifactCmd.SetHelpFunc(func(cmd *cobra.Command, _ []string) {
cmd.Print("help")
})
rootCmd.SetArgs([]string{"job", "artifact"})
_, err := rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal("help"))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("requires at least 1 arg(s), only received 0"))
})
It("should success", func() {
......@@ -72,8 +66,8 @@ var _ = Describe("job artifact command", func() {
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal(`id name path size
n1 a.log a.log 0
Expect(buf.String()).To(Equal(`Name Path Size
a.log a.log 0
`))
})
......@@ -93,8 +87,8 @@ n1 a.log a.log 0
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal(`id name path size
n1 a.log a.log 0
Expect(buf.String()).To(Equal(`Name Path Size
a.log a.log 0
`))
})
......@@ -109,9 +103,8 @@ n1 a.log a.log 0
rootCmd.SetArgs([]string{"job", "artifact", jobName, "invalid"})
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal("strconv.Atoi: parsing \"invalid\": invalid syntax\n"))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("invalid syntax"))
})
})
})
......@@ -3,12 +3,10 @@ package cmd
import (
"encoding/json"
"fmt"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"log"
"net/http"
"strings"
"github.com/AlecAivazis/survey/v2"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
)
......@@ -16,11 +14,10 @@ import (
// JobBuildOption is the job build option
type JobBuildOption struct {
BatchOption
CommonOption
Param string
ParamArray []string
RoundTripper http.RoundTripper
}
var jobBuildOption JobBuildOption
......@@ -32,12 +29,13 @@ func ResetJobBuildOption() {
func init() {
jobCmd.AddCommand(jobBuildCmd)
jobBuildCmd.Flags().BoolVarP(&jobBuildOption.Batch, "batch", "b", false,
i18n.T("Batch mode, no need to confirm"))
jobBuildOption.SetFlag(jobBuildCmd)
jobBuildCmd.Flags().StringVarP(&jobBuildOption.Param, "param", "", "",
i18n.T("Parameters of the job which is JSON format"))
jobBuildCmd.Flags().StringArrayVar(&jobBuildOption.ParamArray, "param-entry", nil,
i18n.T("Parameters of the job which are the entry format, for example: --param-entry name=value"))
jobBuildOption.BatchOption.Stdio = GetSystemStdio()
jobBuildOption.CommonOption.Stdio = GetSystemStdio()
}
var jobBuildCmd = &cobra.Command{
......@@ -63,6 +61,7 @@ You need to give the parameters if your pipeline has them. Learn more about it f
paramDefs = append(paramDefs, client.ParameterDefinition{
Name: entryArray[0],
Value: entryArray[1],
Type: "StringParameterDefinition",
})
}
}
......@@ -76,7 +75,7 @@ You need to give the parameters if your pipeline has them. Learn more about it f
RunE: func(cmd *cobra.Command, args []string) (err error) {
name := args[0]
if !jobBuildOption.Batch && !jobBuildOption.Confirm(fmt.Sprintf("Are you sure to build job %s", name)) {
if !jobBuildOption.Confirm(fmt.Sprintf("Are you sure to build job %s", name)) {
return
}
......@@ -85,7 +84,7 @@ You need to give the parameters if your pipeline has them. Learn more about it f
RoundTripper: jobBuildOption.RoundTripper,
},
}
getCurrentJenkinsAndClientOrDie(&(jclient.JenkinsCore))
getCurrentJenkinsAndClient(&(jclient.JenkinsCore))
paramDefs := []client.ParameterDefinition{}
hasParam := false
......@@ -105,22 +104,12 @@ You need to give the parameters if your pipeline has them. Learn more about it f
continue
}
if data, err := json.MarshalIndent(pro.ParameterDefinitions, "", " "); err == nil {
var data []byte
if data, err = json.MarshalIndent(pro.ParameterDefinitions, "", " "); err == nil {
content := string(data)
prompt := &survey.Editor{
Message: "Edit your pipeline script",
FileName: "*.sh",
Default: content,
HideDefault: true,
AppendDefault: true,
}
if err = survey.AskOne(prompt, &content); err != nil {
log.Fatal(err)
}
if err = json.Unmarshal([]byte(content), &paramDefs); err != nil {
log.Fatal(err)
content, err = jobBuildOption.Editor(content, "Edit your pipeline script")
if err == nil {
err = json.Unmarshal([]byte(content), &paramDefs)
}
}
hasParam = true
......
......@@ -3,13 +3,16 @@ package cmd
import (
"bytes"
"fmt"
"github.com/Netflix/go-expect"
"io/ioutil"
"net/http"
"os"
"testing"
"time"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/golang/mock/gomock"
"github.com/jenkins-zh/jenkins-cli/client"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
......@@ -101,3 +104,85 @@ var _ = Describe("job build command", func() {
})
})
})
func TestBuildJob(t *testing.T) {
RunEditCommandTest(t, EditCommandTest{
ConfirmProcedure: func(c *expect.Console) {
c.ExpectString("Are you sure to build job fake")
c.SendLine("y")
//c.ExpectEOF()
},
Procedure: func(c *expect.Console) {
c.ExpectString("Edit your pipeline script")
c.SendLine("")
go c.ExpectEOF()
time.Sleep(time.Millisecond)
c.Send(`VGdi[{"Description":"","name":"name","Type":"StringParameterDefinition","value":"value","DefaultParameterValue":{"Description":"","Value":null}}]`)
c.Send("\x1b")
c.SendLine(":wq!")
},
CommonOption: &jobBuildOption.CommonOption,
BatchOption: &jobBuildOption.BatchOption,
Test: func(stdio terminal.Stdio) (err error) {
var data []byte
rootOptions.ConfigFile = "test.yaml"
data, err = generateSampleConfig()
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
ctrl := gomock.NewController(t)
roundTripper := mhttp.NewMockRoundTripper(ctrl)
var (
url = "http://localhost:8080/jenkins"
jobName = "fake"
user = "admin"
token = "111e3a2f0231198855dceaff96f20540a9"
)
request, _ := http.NewRequest("GET", fmt.Sprintf("%s/job/%s/api/json",
url, jobName), nil)
request.SetBasicAuth(user, token)
response := &http.Response{
StatusCode: 200,
Proto: "HTTP/1.1",
Request: request,
Body: ioutil.NopCloser(bytes.NewBufferString(`
{"name":"fake",
"property" : [
{
"_class" : "hudson.model.ParametersDefinitionProperty",
"parameterDefinitions" : [
{
"_class" : "hudson.model.StringParameterDefinition",
"defaultParameterValue" : {
"_class" : "hudson.model.StringParameterValue",
"name" : "name",
"value" : "value"
},
"description" : "",
"name" : "name",
"type" : "StringParameterDefinition"
}
]
}
]}
`)),
}
roundTripper.EXPECT().
RoundTrip(request).Return(response, nil)
client.PrepareForBuildWithParams(roundTripper, url, jobName, user, token)
jobBuildOption.RoundTripper = roundTripper
jobBuildOption.BatchOption.Stdio = stdio
jobBuildOption.CommonOption.Stdio = stdio
rootCmd.SetArgs([]string{"job", "build", "fake", "-b=false"})
_, err = rootCmd.ExecuteC()
return
},
})
}
func RunEditCommandTest(t *testing.T, test EditCommandTest) {
RunTest(t, test.Test, test.ConfirmProcedure, test.Procedure)
}
......@@ -3,10 +3,6 @@ package cmd
import (
"fmt"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"net/http"
"github.com/jenkins-zh/jenkins-cli/app/helper"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
)
......@@ -14,8 +10,7 @@ import (
// JobDeleteOption is the job delete option
type JobDeleteOption struct {
BatchOption
RoundTripper http.RoundTripper
CommonOption
}
var jobDeleteOption JobDeleteOption
......@@ -23,6 +18,8 @@ var jobDeleteOption JobDeleteOption
func init() {
jobCmd.AddCommand(jobDeleteCmd)
jobDeleteOption.SetFlag(jobDeleteCmd)
jobDeleteOption.BatchOption.Stdio = GetSystemStdio()
jobDeleteOption.CommonOption.Stdio = GetSystemStdio()
}
var jobDeleteCmd = &cobra.Command{
......@@ -31,7 +28,7 @@ var jobDeleteCmd = &cobra.Command{
Short: i18n.T("Delete a job in your Jenkins"),
Long: i18n.T("Delete a job in your Jenkins"),
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) (err error) {
jobName := args[0]
if !jobDeleteOption.Confirm(fmt.Sprintf("Are you sure to delete job %s ?", jobName)) {
return
......@@ -42,9 +39,9 @@ var jobDeleteCmd = &cobra.Command{
RoundTripper: jobDeleteOption.RoundTripper,
},
}
getCurrentJenkinsAndClientOrDie(&(jclient.JenkinsCore))
getCurrentJenkinsAndClient(&(jclient.JenkinsCore))
err := jclient.Delete(jobName)
helper.CheckErr(cmd, err)
err = jclient.Delete(jobName)
return
},
}
......@@ -3,13 +3,18 @@ package cmd
import (
"bytes"
"fmt"
"github.com/Netflix/go-expect"
"io/ioutil"
"net/http"
"os"
"testing"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/golang/mock/gomock"
"github.com/hinshun/vt10x"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/require"
"github.com/jenkins-zh/jenkins-cli/mock/mhttp"
"github.com/jenkins-zh/jenkins-cli/util"
......@@ -87,3 +92,117 @@ var _ = Describe("job delete command", func() {
})
})
})
type PromptCommandTest struct {
Message string
MsgConfirm MsgConfirm
BatchOption *BatchOption
Procedure func(*expect.Console)
Args []string
}
type EditCommandTest struct {
Message string
DefaultContent string
EditContent EditContent
CommonOption *CommonOption
BatchOption *BatchOption
ConfirmProcedure func(*expect.Console)
Procedure func(*expect.Console)
Test func(stdio terminal.Stdio) (err error)
Expected string
Args []string
}
type PromptTest struct {
Message string
MsgConfirm MsgConfirm
Procedure func(*expect.Console)
Expected interface{}
}
type EditorTest struct {
Message string
DefaultContent string
EditContent EditContent
Procedure func(*expect.Console)
Expected string
}
func RunPromptCommandTest(t *testing.T, test PromptCommandTest) {
RunTest(t, func(stdio terminal.Stdio) (err error) {
var data []byte
data, err = generateSampleConfig()
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
test.BatchOption.Stdio = stdio
rootOptions.ConfigFile = "test.yaml"
rootCmd.SetArgs(test.Args)
_, err = rootCmd.ExecuteC()
return
}, test.Procedure)
}
func RunPromptTest(t *testing.T, test PromptTest) {
var answer interface{}
RunTest(t, func(stdio terminal.Stdio) error {
batch := &BatchOption{
Batch: false,
Stdio: stdio,
}
answer = batch.Confirm(test.Message)
return nil
}, test.Procedure)
require.Equal(t, test.Expected, answer)
}
func RunEditorTest(t *testing.T, test EditorTest) {
var content string
RunTest(t, func(stdio terminal.Stdio) (err error) {
editor := &CommonOption{
Stdio: stdio,
}
content, err = editor.Editor(test.DefaultContent, test.Message)
return nil
}, test.Procedure)
require.Equal(t, test.Expected, content)
}
func Stdio(c *expect.Console) terminal.Stdio {
return terminal.Stdio{In: c.Tty(), Out: c.Tty(), Err: c.Tty()}
}
func RunTest(t *testing.T, test func(terminal.Stdio) error, procedures ...func(*expect.Console)) {
//t.Parallel()
// Multiplex output to a buffer as well for the raw bytes.
buf := new(bytes.Buffer)
//c, err := expect.NewConsole(expect.WithStdout(buf))
//c, err := expect.NewConsole(expect.WithStdout(os.Stdout))
c, _, err := vt10x.NewVT10XConsole(expect.WithStdout(buf))
require.Nil(t, err)
defer c.Close()
donec := make(chan struct{})
go func() {
defer close(donec)
for _, procedure := range procedures {
if procedure != nil {
procedure(c)
}
}
}()
err = test(Stdio(c))
//fmt.Println("Raw output: ", buf.String())
require.Nil(t, err)
// Close the slave end of the pty, and read the remaining bytes from the master end.
c.Tty().Close()
<-donec
// Dump the terminal's screen.
//fmt.Sprintf("\n%s", expect.StripTrailingEmptyLines(state.String()))
}
package cmd
import (
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"io/ioutil"
"net/http"
"github.com/AlecAivazis/survey/v2"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
)
// JobEditOption is the option for job create command
type JobEditOption struct {
CommonOption
Filename string
Script string
URL string
RoundTripper http.RoundTripper
}
var jobEditOption JobEditOption
......@@ -29,6 +29,7 @@ func init() {
i18n.T("Filename to files to use to replace pipeline"))
jobEditCmd.Flags().StringVarP(&jobEditOption.Script, "script", "s", "",
i18n.T("Script to use to replace pipeline. Use script first if you give filename at the meantime."))
jobEditOption.Stdio = GetSystemStdio()
}
var jobEditCmd = &cobra.Command{
......@@ -76,24 +77,11 @@ func (j *JobEditOption) getPipeline(jClient *client.JobClient, name string) (scr
if job != nil {
content = job.Script
}
script, err = modifyScript(content)
script, err = j.Editor(content, "Edit your pipeline script")
}
return
}
func modifyScript(script string) (content string, err error) {
prompt := &survey.Editor{
Message: "Edit your pipeline script",
FileName: "*.sh",
Default: script,
HideDefault: true,
AppendDefault: true,
}
err = survey.AskOne(prompt, &content)
return
}
func (j *JobEditOption) getPipelineFromFile() (script string, err error) {
if j.Filename == "" {
return
......
......@@ -3,13 +3,13 @@ package cmd
import (
"encoding/json"
"fmt"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"log"
"net/http"
"strconv"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
)
......@@ -21,7 +21,6 @@ type JobInputOption struct {
Action string
RoundTripper http.RoundTripper
Stdio terminal.Stdio
}
var jobInputOption JobInputOption
......@@ -30,6 +29,7 @@ func init() {
jobCmd.AddCommand(jobInputCmd)
jobInputCmd.Flags().StringVarP(&jobInputOption.Action, "action", "", "",
i18n.T("The action whether you want to process or abort."))
jobInputOption.Stdio = GetSystemStdio()
}
var jobInputCmd = &cobra.Command{
......@@ -66,20 +66,9 @@ var jobInputCmd = &cobra.Command{
inputsJSON, _ := json.MarshalIndent(inputAction.Inputs, "", " ")
content := string(inputsJSON)
prompt := &survey.Editor{
Message: "Edit your pipeline input parameters",
FileName: "*.json",
Default: content,
HideDefault: true,
AppendDefault: true,
}
if err = survey.AskOne(prompt, &content); err != nil {
log.Fatal(err)
}
if err = json.Unmarshal([]byte(content), &(inputAction.Inputs)); err != nil {
log.Fatal(err)
content, err = jobBuildOption.Editor(content, "Edit your pipeline input parameters")
if err == nil {
err = json.Unmarshal([]byte(content), &(inputAction.Inputs))
}
for _, input := range inputAction.Inputs {
......
......@@ -5,15 +5,13 @@ import (
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
"net/http"
"strconv"
)
// JobStopOption is the job stop option
type JobStopOption struct {
BatchOption
RoundTripper http.RoundTripper
CommonOption
}
var jobStopOption JobStopOption
......@@ -21,6 +19,8 @@ var jobStopOption JobStopOption
func init() {
jobCmd.AddCommand(jobStopCmd)
jobStopOption.SetFlag(jobStopCmd)
jobStopOption.CommonOption.Stdio = GetSystemStdio()
jobStopOption.BatchOption.Stdio = GetSystemStdio()
}
var jobStopCmd = &cobra.Command{
......
......@@ -3,13 +3,12 @@ package cmd
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"io/ioutil"
"net/http"
"os"
"github.com/jenkins-zh/jenkins-cli/mock/mhttp"
)
......
package cmd
import (
"bytes"
"fmt"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"net/http"
"github.com/jenkins-zh/jenkins-cli/app/helper"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/jenkins-zh/jenkins-cli/util"
"github.com/spf13/cobra"
)
// JobTypeOption is the job type cmd option
type JobTypeOption struct {
OutputOption
RoundTripper http.RoundTripper
CommonOption
}
var jobTypeOption JobTypeOption
func init() {
jobCmd.AddCommand(jobTypeCmd)
jobTypeCmd.Flags().StringVarP(&jobTypeOption.Format, "output", "o", "table", "Format the output")
jobTypeOption.SetFlagWithHeaders(jobTypeCmd, "DisplayName,Class")
}
var jobTypeCmd = &cobra.Command{
Use: "type",
Short: i18n.T("Print the types of job which in your Jenkins"),
Long: i18n.T("Print the types of job which in your Jenkins"),
Run: func(cmd *cobra.Command, _ []string) {
RunE: func(cmd *cobra.Command, _ []string) (err error) {
jclient := &client.JobClient{
JenkinsCore: client.JenkinsCore{
RoundTripper: jobTypeOption.RoundTripper,
......@@ -39,15 +32,19 @@ var jobTypeCmd = &cobra.Command{
}
getCurrentJenkinsAndClientOrDie(&(jclient.JenkinsCore))
status, err := jclient.GetJobTypeCategories()
var jobCategories []client.JobCategory
jobCategories, err = jclient.GetJobTypeCategories()
if err == nil {
var data []byte
data, err = jobTypeOption.Output(status)
if err == nil && len(data) > 0 {
cmd.Print(string(data))
var jobCategoryItems []client.JobCategoryItem
for _, jobCategory := range jobCategories {
for _, item := range jobCategory.Items {
jobCategoryItems = append(jobCategoryItems, item)
}
}
jobTypeOption.Writer = cmd.OutOrStdout()
err = jobTypeOption.OutputV2(jobCategoryItems)
}
helper.CheckErr(cmd, err)
return
},
}
......@@ -72,24 +69,3 @@ func GetCategories(jclient *client.JobClient) (
}
return
}
// Output renders data into a table
func (o *JobTypeOption) Output(obj interface{}) (data []byte, err error) {
if data, err = o.OutputOption.Output(obj); err != nil {
buf := new(bytes.Buffer)
jobCategories := obj.([]client.JobCategory)
table := util.CreateTable(buf)
table.AddRow("number", "name", "type")
for _, jobCategory := range jobCategories {
for i, item := range jobCategory.Items {
table.AddRow(fmt.Sprintf("%d", i), item.DisplayName,
jobCategory.Name)
}
}
table.Render()
err = nil
data = buf.Bytes()
}
return
}
......@@ -101,7 +101,7 @@ var _ = Describe("job type command", func() {
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal("number name type\n"))
Expect(buf.String()).To(Equal("DisplayName Class\n"))
})
It("should success, empty list", func() {
......@@ -128,7 +128,7 @@ var _ = Describe("job type command", func() {
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal("number name type\n"))
Expect(buf.String()).To(Equal("DisplayName Class\n"))
})
It("should success, one item", func() {
......@@ -173,8 +173,8 @@ var _ = Describe("job type command", func() {
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal(`number name type
0 displayName Nested Projects
Expect(buf.String()).To(Equal(`DisplayName Class
displayName class
`))
})
})
......
......@@ -2,7 +2,6 @@ package cmd
import (
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/util"
"github.com/spf13/cobra"
......@@ -10,27 +9,24 @@ import (
// OpenOption is the open cmd option
type OpenOption struct {
CommonOption
InteractiveOption
Name string
Config bool
ExecContext util.ExecContext
}
var openOption OpenOption
func init() {
rootCmd.AddCommand(openCmd)
openCmd.Flags().StringVarP(&openOption.Name, "name", "n", "",
i18n.T("Open a specific Jenkins by name"))
openCmd.Flags().BoolVarP(&openOption.Config, "config", "c", false,
i18n.T("Open the configuration page of Jenkins"))
openOption.SetFlag(openCmd)
openOption.Stdio = GetSystemStdio()
}
var openCmd = &cobra.Command{
Use: "open [config name]",
Use: "open",
Short: i18n.T("Open your Jenkins with a browser"),
Long: i18n.T(`Open your Jenkins with a browser`),
Example: `jcli open -n [config name]`,
......@@ -40,35 +36,30 @@ var openCmd = &cobra.Command{
var configName string
if len(args) > 0 {
configName = args[0]
} else if openOption.Name != "" {
configName = openOption.Name
}
if configName == "" && openOption.Interactive {
jenkinsNames := getJenkinsNames()
prompt := &survey.Select{
Message: i18n.T("Choose a Jenkins which you want to open:"),
Options: jenkinsNames,
}
if err = survey.AskOne(prompt, &(configName)); err != nil {
return
}
configName, err = openOption.Select(jenkinsNames,
i18n.T("Choose a Jenkins which you want to open:"), "")
}
if configName != "" {
jenkins = findJenkinsByName(configName)
} else {
jenkins = getCurrentJenkins()
}
if err == nil {
if configName != "" {
jenkins = findJenkinsByName(configName)
} else {
jenkins = getCurrentJenkins()
}
if jenkins != nil && jenkins.URL != "" {
url := jenkins.URL
if openOption.Config {
url = fmt.Sprintf("%s/configure", url)
if jenkins != nil && jenkins.URL != "" {
url := jenkins.URL
if openOption.Config {
url = fmt.Sprintf("%s/configure", url)
}
err = util.Open(url, openOption.ExecContext)
} else {
err = fmt.Errorf("no URL found with Jenkins %s", configName)
}
err = util.Open(url, openOption.ExecContext)
} else {
err = fmt.Errorf("no URL found with Jenkins %s", configName)
}
return
},
......
......@@ -3,34 +3,46 @@ package cmd
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/Netflix/go-expect"
"github.com/jenkins-zh/jenkins-cli/util"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"io/ioutil"
"os"
"path"
"testing"
)
var _ = Describe("test open", func() {
var (
err error
jenkinsName string
configFile string
cmdArgs []string
)
BeforeEach(func() {
configFile = path.Join(os.TempDir(), "fake.yaml")
data, err := generateSampleConfig()
Expect(err).To(BeNil())
rootOptions.ConfigFile = "test.yaml"
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
err = ioutil.WriteFile(configFile, data, 0664)
Expect(err).To(BeNil())
openOption.ExecContext = util.FakeExecCommandSuccess
jenkinsName = "fake"
cmdArgs = []string{"open", jenkinsName, "--configFile", configFile}
})
AfterEach(func() {
os.Remove(configFile)
})
JustBeforeEach(func() {
buf := new(bytes.Buffer)
rootCmd.SetOut(buf)
rootCmd.SetArgs([]string{"open", jenkinsName})
rootCmd.SetArgs(cmdArgs)
_, err = rootCmd.ExecuteC()
})
......@@ -41,7 +53,7 @@ var _ = Describe("test open", func() {
Context("give a right config", func() {
BeforeEach(func() {
jenkinsName = "yourServer"
cmdArgs = []string{"open", "yourServer", "--configFile", configFile}
})
It("should success", func() {
......@@ -52,4 +64,46 @@ var _ = Describe("test open", func() {
os.Remove(rootOptions.ConfigFile)
})
})
Context("open the current jenkins with config page", func() {
BeforeEach(func() {
cmdArgs = []string{"open", "--interactive=false", "--config", "--configFile", configFile}
})
It("should success", func() {
Expect(err).NotTo(HaveOccurred())
})
})
})
func TestOpenJenkins(t *testing.T) {
RunEditCommandTest(t, EditCommandTest{
ConfirmProcedure: func(c *expect.Console) {
c.ExpectString("Choose a Jenkins which you want to open:")
// filter away everything
c.SendLine("z")
// send enter (should get ignored since there are no answers)
c.SendLine(string(terminal.KeyEnter))
// remove the filter we just applied
c.SendLine(string(terminal.KeyBackspace))
// press enter
c.SendLine(string(terminal.KeyEnter))
},
Test: func(stdio terminal.Stdio) (err error) {
configFile := path.Join(os.TempDir(), "fake.yaml")
defer os.Remove(configFile)
var data []byte
data, err = generateSampleConfig()
err = ioutil.WriteFile(configFile, data, 0664)
openOption.ExecContext = util.FakeExecCommandSuccess
openOption.CommonOption.Stdio = stdio
rootCmd.SetArgs([]string{"open", "--interactive", "--configFile", configFile})
_, err = rootCmd.ExecuteC()
return
},
})
}
......@@ -2,9 +2,6 @@ package cmd
import (
"fmt"
"net/http"
"github.com/AlecAivazis/survey/v2"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
......@@ -13,48 +10,39 @@ import (
// RestartOption holds the options for restart cmd
type RestartOption struct {
BatchOption
RoundTripper http.RoundTripper
CommonOption
}
var restartOption RestartOption
func init() {
rootCmd.AddCommand(restartCmd)
restartCmd.Flags().BoolVarP(&restartOption.Batch, "batch", "b", false, "Batch mode, no need confirm")
restartOption.SetFlag(restartCmd)
restartOption.BatchOption.Stdio = GetSystemStdio()
restartOption.CommonOption.Stdio = GetSystemStdio()
}
var restartCmd = &cobra.Command{
Use: "restart",
Short: i18n.T("Restart your Jenkins"),
Long: i18n.T("Restart your Jenkins"),
Run: func(cmd *cobra.Command, _ []string) {
jenkins := getCurrentJenkinsFromOptionsOrDie()
if !restartOption.Batch {
confirm := false
prompt := &survey.Confirm{
Message: fmt.Sprintf("Are you sure to restart Jenkins %s?", jenkins.URL),
}
if err := survey.AskOne(prompt, &confirm); !confirm {
return
} else if err != nil {
cmd.PrintErrln(err)
return
}
RunE: func(cmd *cobra.Command, _ []string) (err error) {
jenkins := getCurrentJenkinsFromOptions()
if !restartOption.Confirm(fmt.Sprintf("Are you sure to restart Jenkins %s?", jenkins.URL)) {
return
}
jclient := &client.CoreClient{
jClient := &client.CoreClient{
JenkinsCore: client.JenkinsCore{
RoundTripper: restartOption.RoundTripper,
Debug: rootOptions.Debug,
},
}
getCurrentJenkinsAndClientOrDie(&(jclient.JenkinsCore))
getCurrentJenkinsAndClient(&(jClient.JenkinsCore))
if err := jclient.Restart(); err == nil {
if err = jClient.Restart(); err == nil {
cmd.Println("Please wait while Jenkins is restarting")
} else {
cmd.PrintErrln(err)
}
return
},
}
......@@ -66,9 +66,8 @@ var _ = Describe("restart command", func() {
buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal("bad request, code 400\n"))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("bad request, code 400"))
})
})
})
......@@ -2,7 +2,7 @@ package cmd
import (
"fmt"
"github.com/jenkins-zh/jenkins-cli/app/helper"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"net/http"
"github.com/jenkins-zh/jenkins-cli/client"
......@@ -20,7 +20,9 @@ var userDeleteOption UserDeleteOption
func init() {
userCmd.AddCommand(userDeleteCmd)
userDeleteCmd.Flags().BoolVarP(&userDeleteOption.Batch, "batch", "b", false, "Batch mode, no need confirm")
userDeleteCmd.Flags().BoolVarP(&userDeleteOption.Batch, "batch", "b", false,
i18n.T("Batch mode, no need confirm"))
userDeleteOption.BatchOption.Stdio = GetSystemStdio()
}
var userDeleteCmd = &cobra.Command{
......@@ -29,7 +31,7 @@ var userDeleteCmd = &cobra.Command{
Short: "Delete a user for your Jenkins",
Long: `Delete a user for your Jenkins`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) (err error) {
username := args[0]
if !userDeleteOption.Confirm(fmt.Sprintf("Are you sure to delete user %s ?", username)) {
......@@ -43,8 +45,6 @@ var userDeleteCmd = &cobra.Command{
},
}
getCurrentJenkinsAndClientOrDie(&(jclient.JenkinsCore))
err := jclient.Delete(username)
helper.CheckErr(cmd, err)
return jclient.Delete(username)
},
}
......@@ -5,6 +5,7 @@ import (
"github.com/jenkins-zh/jenkins-cli/client"
"io/ioutil"
"os"
"testing"
"github.com/golang/mock/gomock"
"github.com/jenkins-zh/jenkins-cli/mock/mhttp"
......@@ -72,9 +73,11 @@ var _ = Describe("user delete command", func() {
buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal("error: unexpected status code: 500"))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("unexpected status code: 500"))
})
})
})
func TestDeleteUser(t *testing.T) {
}
package cmd
import (
"log"
"github.com/AlecAivazis/survey/v2"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra"
)
// UserEditOption is the user edit cmd option
type UserEditOption struct {
Description bool
CommonOption
Description string
}
var userEditOption UserEditOption
func init() {
userCmd.AddCommand(userEditCmd)
userEditCmd.Flags().BoolVarP(&userEditOption.Description, "desc", "d", false, "Edit the description")
userEditCmd.Flags().StringVarP(&userEditOption.Description, "desc", "d", "",
i18n.T("Edit the description"))
userEditOption.Stdio = GetSystemStdio()
}
var userEditCmd = &cobra.Command{
Use: "edit",
Short: "Edit the user of your Jenkins",
Long: `Edit the user of your Jenkins`,
Run: func(_ *cobra.Command, _ []string) {
jenkins := getCurrentJenkinsFromOptionsOrDie()
jclient := &client.UserClient{}
jclient.URL = jenkins.URL
jclient.UserName = jenkins.UserName
jclient.Token = jenkins.Token
jclient.Proxy = jenkins.Proxy
jclient.ProxyAuth = jenkins.ProxyAuth
if status, err := jclient.Get(); err == nil {
description := status.Description
prompt := &survey.Editor{
Message: "Edit user description",
FileName: "*.sh",
Default: description,
HideDefault: true,
AppendDefault: true,
}
if err = survey.AskOne(prompt, &description); err != nil {
log.Fatal(err)
} else {
if err = jclient.EditDesc(description); err != nil {
log.Fatal(err)
}
RunE: func(_ *cobra.Command, _ []string) (err error) {
jClient := &client.UserClient{
JenkinsCore: client.JenkinsCore{
RoundTripper: userEditOption.RoundTripper,
},
}
getCurrentJenkinsAndClient(&(jClient.JenkinsCore))
var user *client.User
if user, err = jClient.Get(); err == nil {
var content string
content, err = userEditOption.Editor(user.Description, "Edit user description")
if err == nil {
err = jClient.EditDesc(content)
}
} else {
log.Fatal(err)
}
return
},
}
package cmd
import (
"github.com/Netflix/go-expect"
"io/ioutil"
"os"
"path"
"testing"
"time"
"github.com/AlecAivazis/survey/v2/terminal"
"github.com/golang/mock/gomock"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/jenkins-zh/jenkins-cli/mock/mhttp"
)
func TestEditUser(t *testing.T) {
RunEditCommandTest(t, EditCommandTest{
Procedure: func(c *expect.Console) {
c.ExpectString("Edit user description")
c.SendLine("")
go c.ExpectEOF()
time.Sleep(time.Millisecond)
c.Send("\x1b")
c.SendLine(":wq!")
},
Test: func(stdio terminal.Stdio) (err error) {
configFile := path.Join(os.TempDir(), "fake.yaml")
defer os.Remove(configFile)
data, err := generateSampleConfig()
err = ioutil.WriteFile(configFile, data, 0664)
var (
description = "fake-description\n"
)
ctrl := gomock.NewController(t)
roundTripper := mhttp.NewMockRoundTripper(ctrl)
client.PrepareGetUser(roundTripper, "http://localhost:8080/jenkins", "admin", "111e3a2f0231198855dceaff96f20540a9")
client.PrepareForEditUserDesc(roundTripper, "http://localhost:8080/jenkins",
"admin", description, "admin", "111e3a2f0231198855dceaff96f20540a9")
rootCmd.SetArgs([]string{"user", "edit", "--desc", description, "--configFile", configFile})
userEditOption.RoundTripper = roundTripper
userEditOption.CommonOption.Stdio = stdio
_, err = rootCmd.ExecuteC()
return
},
})
}
......@@ -55,7 +55,7 @@ var _ = Describe("user command", func() {
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal("{\n \"absoluteUrl\": \"\",\n \"Description\": \"\",\n \"fullname\": \"admin\",\n \"ID\": \"\"\n}\n"))
Expect(buf.String()).To(Equal("{\n \"absoluteUrl\": \"\",\n \"Description\": \"fake-description\",\n \"fullname\": \"admin\",\n \"ID\": \"\"\n}\n"))
})
It("with status code 500", func() {
......
......@@ -170,6 +170,7 @@ var _ = Describe("job test", func() {
err := jobClient.BuildWithParams(jobName, []ParameterDefinition{ParameterDefinition{
Name: "name",
Value: "value",
Type: "StringParameterDefinition",
}})
Expect(err).To(BeNil())
})
......
......@@ -64,7 +64,7 @@ func PrepareForBuildWithNoParams(roundTripper *mhttp.MockRoundTripper, rootURL,
// PrepareForBuildWithParams only for test
func PrepareForBuildWithParams(roundTripper *mhttp.MockRoundTripper, rootURL, jobName, user, password string) (
request *http.Request, response *http.Response) {
formData := url.Values{"json": {`{"parameter": {"Description":"","name":"name","Type":"","value":"value","DefaultParameterValue":{"Description":"","Value":null}}}`}}
formData := url.Values{"json": {`{"parameter": {"Description":"","name":"name","Type":"StringParameterDefinition","value":"value","DefaultParameterValue":{"Description":"","Value":null}}}`}}
payload := strings.NewReader(formData.Encode())
request, _ = http.NewRequest("POST", fmt.Sprintf("%s/job/%s/build", rootURL, jobName), payload)
request.Header.Add(util.ContentType, util.ApplicationForm)
......
......@@ -18,7 +18,7 @@ func PrepareGetUser(roundTripper *mhttp.MockRoundTripper, rootURL, user, passwd
response = &http.Response{
StatusCode: 200,
Request: request,
Body: ioutil.NopCloser(bytes.NewBufferString(`{"fullName":"admin"}`)),
Body: ioutil.NopCloser(bytes.NewBufferString(`{"fullName":"admin","description":"fake-description"}`)),
}
roundTripper.EXPECT().
RoundTrip(request).Return(response, nil)
......
......@@ -43,6 +43,7 @@ github.com/gosuri/uiprogress v0.0.1 h1:0kpv/XY/qTmFWl/SkaJykZXrBBzwwadmW8fRb7RJS
github.com/gosuri/uiprogress v0.0.1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
......@@ -58,6 +59,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
......@@ -69,6 +71,7 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
......@@ -79,6 +82,7 @@ github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
......@@ -87,13 +91,17 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册