diff --git a/app/cmd/job_build.go b/app/cmd/job_build.go index 41becd3e0b0700dd993e4c7821d3dbbb33c9b6a6..e41786b18f518557c90989cdd7c77d4f1288b244 100644 --- a/app/cmd/job_build.go +++ b/app/cmd/job_build.go @@ -3,11 +3,10 @@ package cmd import ( "encoding/json" "fmt" - "github.com/jenkins-zh/jenkins-cli/app/cmd/common" "strings" + "github.com/jenkins-zh/jenkins-cli/app/cmd/common" "github.com/jenkins-zh/jenkins-cli/app/i18n" - "github.com/jenkins-zh/jenkins-cli/client" "github.com/spf13/cobra" ) @@ -19,6 +18,8 @@ type JobBuildOption struct { Param string ParamArray []string + + ParamFilePathArray []string } var jobBuildOption JobBuildOption @@ -35,6 +36,9 @@ func init() { 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")) + jobBuildCmd.Flags().StringArrayVar(&jobBuildOption.ParamFilePathArray, "param-file", nil, + i18n.T("Parameters of the job which is file path, for example: --param-file name=filename")) + jobBuildOption.BatchOption.Stdio = common.GetSystemStdio() jobBuildOption.CommonOption.Stdio = common.GetSystemStdio() } @@ -45,8 +49,8 @@ var jobBuildCmd = &cobra.Command{ Long: i18n.T(`Build the job of your Jenkins. You need to give the parameters if your pipeline has them. Learn more about it from https://jenkins.io/doc/book/pipeline/syntax/#parameters.`), Args: cobra.MinimumNArgs(1), - PreRunE: func(cmd *cobra.Command, args []string) (err error) { - if jobBuildOption.ParamArray == nil { + PreRunE: func(_ *cobra.Command, _ []string) (err error) { + if jobBuildOption.ParamArray == nil && jobBuildOption.ParamFilePathArray == nil { return } @@ -67,13 +71,23 @@ You need to give the parameters if your pipeline has them. Learn more about it f } } + for _, filepathEntry := range jobBuildOption.ParamFilePathArray { + if filepathArray := strings.SplitN(filepathEntry, "=", 2); len(filepathArray) == 2 { + paramDefs = append(paramDefs, client.ParameterDefinition{ + Name: filepathArray[0], + Filepath: filepathArray[1], + Type: client.FileParameterDefinition, + }) + } + } + var data []byte if data, err = json.Marshal(paramDefs); err == nil { jobBuildOption.Param = string(data) } return }, - RunE: func(cmd *cobra.Command, args []string) (err error) { + RunE: func(_ *cobra.Command, args []string) (err error) { name := args[0] if !jobBuildOption.Confirm(fmt.Sprintf("Are you sure to build job %s", name)) { diff --git a/app/cmd/job_build_test.go b/app/cmd/job_build_test.go index 219dc5e536eae41a48207b84d75c15e465dc4e8a..d03fdf37bbc0dc1eee98d69656216a7a13141f59 100644 --- a/app/cmd/job_build_test.go +++ b/app/cmd/job_build_test.go @@ -3,14 +3,15 @@ package cmd import ( "bytes" "fmt" + "io/ioutil" + "net/http" + "os" + "github.com/golang/mock/gomock" "github.com/jenkins-zh/jenkins-cli/client" "github.com/jenkins-zh/jenkins-cli/mock/mhttp" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "io/ioutil" - "net/http" - "os" ) var _ = Describe("job build command", func() { @@ -102,6 +103,22 @@ var _ = Describe("job build command", func() { _, err = rootCmd.ExecuteC() Expect(err).NotTo(HaveOccurred()) }) + + /* FIXME: fix the test case + It("with --param-file", func() { + data, err := generateSampleConfig() + Expect(err).To(BeNil()) + err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664) + Expect(err).To(BeNil()) + + client.PrepareForBuildWithParams(roundTripper, "http://localhost:8080/jenkins", jobName, + "admin", "111e3a2f0231198855dceaff96f20540a9") + + rootCmd.SetArgs([]string{"job", "build", jobName, "--param-file", "sample=/tmp/sample.txt", "-b", "true", "--param", ""}) + _, err = rootCmd.ExecuteC() + Expect(err).NotTo(HaveOccurred()) + }) + */ }) }) diff --git a/app/i18n/bindata.go b/app/i18n/bindata.go index ba6efe49c77152b5cbaee8d9b8294f8842ff936b..574289c731bed54e44ca30bd3cb7e2537135eda1 100644 --- a/app/i18n/bindata.go +++ b/app/i18n/bindata.go @@ -215,11 +215,11 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "jcli": &bintree{nil, map[string]*bintree{ - "zh_CN": &bintree{nil, map[string]*bintree{ - "LC_MESSAGES": &bintree{nil, map[string]*bintree{ - "jcli.mo": &bintree{jcliZh_cnLc_messagesJcliMo, map[string]*bintree{}}, - "jcli.po": &bintree{jcliZh_cnLc_messagesJcliPo, map[string]*bintree{}}, + "jcli": {nil, map[string]*bintree{ + "zh_CN": {nil, map[string]*bintree{ + "LC_MESSAGES": {nil, map[string]*bintree{ + "jcli.mo": {jcliZh_cnLc_messagesJcliMo, map[string]*bintree{}}, + "jcli.po": {jcliZh_cnLc_messagesJcliPo, map[string]*bintree{}}, }}, }}, }}, diff --git a/client/job.go b/client/job.go index b84a35df2da67b363e6e290e63fc0ec8aa8d8957..3cb6351ab6b5a911b3b38c47250afd9da372f5de 100644 --- a/client/job.go +++ b/client/job.go @@ -1,19 +1,28 @@ package client import ( + "bytes" "encoding/json" "fmt" - "go.uber.org/zap" + "io" "io/ioutil" - "moul.io/http2curl" + "mime/multipart" "net/http" "net/url" + "os" + "path/filepath" "strconv" "strings" + "go.uber.org/zap" + "moul.io/http2curl" + "github.com/jenkins-zh/jenkins-cli/util" ) +// FileParameterDefinition is the definition for file parameter +const FileParameterDefinition = "FileParameterDefinition" + // JobClient is client for operate jobs type JobClient struct { JenkinsCore @@ -55,20 +64,55 @@ func (q *JobClient) BuildWithParams(jobName string, parameters []ParameterDefini path := ParseJobPath(jobName) api := fmt.Sprintf("%s/build", path) + fileParameters := make([]ParameterDefinition, 0, len(parameters)) + for _, parameter := range parameters { + if parameter.Type == FileParameterDefinition { + fileParameters = append(fileParameters, parameter) + } + } + + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + defer writer.Close() + + // upload file + for _, parameter := range fileParameters { + var file *os.File + file, err = os.Open(parameter.Filepath) + if err != nil { + return err + } + defer file.Close() + + var fWriter io.Writer + fWriter, err = writer.CreateFormFile(parameter.Filepath, filepath.Base(parameter.Filepath)) + if err != nil { + return err + } + _, err = io.Copy(fWriter, file) + } + var paramJSON []byte if len(parameters) == 1 { paramJSON, err = json.Marshal(parameters[0]) } else { paramJSON, err = json.Marshal(parameters) } + if err != nil { + return err + } - if err == nil { - formData := url.Values{"json": {fmt.Sprintf("{\"parameter\": %s}", string(paramJSON))}} - payload := strings.NewReader(formData.Encode()) + if err = writer.WriteField("json", fmt.Sprintf("{\"parameter\": %s}", string(paramJSON))); err != nil { + return err + } - _, err = q.RequestWithoutData("POST", api, - map[string]string{util.ContentType: util.ApplicationForm}, payload, 201) + if err = writer.Close(); err != nil { + return err } + + _, err = q.RequestWithoutData("POST", api, + map[string]string{util.ContentType: writer.FormDataContentType()}, body, 201) + return } @@ -377,6 +421,7 @@ type ParameterDefinition struct { Name string `json:"name"` Type string Value string `json:"value"` + Filepath string `json:"file"` DefaultParameterValue DefaultParameterValue }