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

Add support to upload plugins from local or remote (#79)

上级 d55bde46
...@@ -22,14 +22,17 @@ func init() { ...@@ -22,14 +22,17 @@ func init() {
} }
var configCmd = &cobra.Command{ var configCmd = &cobra.Command{
Use: "config", Use: "config",
Short: "Manage the config of jcli", Aliases: []string{"cfg"},
Long: `Manage the config of jcli`, Short: "Manage the config of jcli",
Long: `Manage the config of jcli`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
current := getCurrentJenkins() current := getCurrentJenkins()
fmt.Printf("Current Jenkins's name is %s, url is %s\n", current.Name, current.URL) fmt.Printf("Current Jenkins's name is %s, url is %s\n", current.Name, current.URL)
}, },
Example: "jcli config -l", Example: ` jcli config generate
jcli config list
jcli config edit`,
} }
// JenkinsServer holds the configuration of your Jenkins // JenkinsServer holds the configuration of your Jenkins
......
...@@ -24,9 +24,10 @@ func init() { ...@@ -24,9 +24,10 @@ func init() {
} }
var configGenerateCmd = &cobra.Command{ var configGenerateCmd = &cobra.Command{
Use: "generate", Use: "generate",
Short: "Generate a sample config file for you", Aliases: []string{"gen"},
Long: `Generate a sample config file for you`, Short: "Generate a sample config file for you",
Long: `Generate a sample config file for you`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if data, err := generateSampleConfig(); err == nil { if data, err := generateSampleConfig(); err == nil {
configPath := configOptions.ConfigFileLocation configPath := configOptions.ConfigFileLocation
......
...@@ -18,6 +18,9 @@ var pluginCmd = &cobra.Command{ ...@@ -18,6 +18,9 @@ var pluginCmd = &cobra.Command{
Use: "plugin", Use: "plugin",
Short: "Manage the plugins of Jenkins", Short: "Manage the plugins of Jenkins",
Long: `Manage the plugins of Jenkins`, Long: `Manage the plugins of Jenkins`,
Example: ` jcli plugin list
jcli plugin search github
jcli plugin check`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd.Help() cmd.Help()
}, },
......
...@@ -28,6 +28,8 @@ var pluginListCmd = &cobra.Command{ ...@@ -28,6 +28,8 @@ var pluginListCmd = &cobra.Command{
Use: "list", Use: "list",
Short: "Print all the plugins which are installed", Short: "Print all the plugins which are installed",
Long: `Print all the plugins which are installed`, Long: `Print all the plugins which are installed`,
Example: ` jcli plugin list --filter name=github
jcli plugin list --filter hasUpdate`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
jenkins := getCurrentJenkins() jenkins := getCurrentJenkins()
jclient := &client.PluginManager{} jclient := &client.PluginManager{}
......
package cmd package cmd
import ( import (
"github.com/jenkins-zh/jenkins-cli/client" "fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"github.com/jenkins-zh/jenkins-cli/util"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// PluginUploadOption will hold the options of plugin cmd
type PluginUploadOption struct {
Remote string
RemoteUser string
RemotePassword string
RemoteJenkins string
pluginFilePath string
}
var pluginUploadOption PluginUploadOption
func init() { func init() {
pluginCmd.AddCommand(pluginUploadCmd) pluginCmd.AddCommand(pluginUploadCmd)
pluginUploadCmd.Flags().StringVarP(&pluginUploadOption.Remote, "remote", "r", "", "Remote plugin URL")
pluginUploadCmd.Flags().StringVarP(&pluginUploadOption.RemoteUser, "remote-user", "", "", "User of remote plugin URL")
pluginUploadCmd.Flags().StringVarP(&pluginUploadOption.RemotePassword, "remote-password", "", "", "Password of remote plugin URL")
pluginUploadCmd.Flags().StringVarP(&pluginUploadOption.RemoteJenkins, "remote-jenkins", "", "", "Remote Jenkins which will find from config list")
} }
var pluginUploadCmd = &cobra.Command{ var pluginUploadCmd = &cobra.Command{
Use: "upload", Use: "upload",
Short: "Upload the plugin from local to your Jenkins", Aliases: []string{"up"},
Long: `Upload the plugin from local to your Jenkins`, Short: "Upload a plugin to your Jenkins",
Long: `Upload a plugin from local filesystem or remote URL to your Jenkins`,
Example: ` jcli plugin upload --remote https://server/sample.hpi
jcli plugin upload sample.hpi`,
PreRun: func(cmd *cobra.Command, args []string) {
if pluginUploadOption.Remote != "" {
file, err := ioutil.TempFile(".", "jcli-plugin")
if err != nil {
log.Fatal(err)
}
defer os.Remove(file.Name())
if pluginUploadOption.RemoteJenkins != "" {
if jenkins := findJenkinsByName(pluginUploadOption.RemoteJenkins); jenkins != nil {
pluginUploadOption.RemoteUser = jenkins.UserName
pluginUploadOption.RemotePassword = jenkins.Token
}
}
pluginUploadOption.pluginFilePath = fmt.Sprintf("%s.hpi", file.Name())
downloader := util.HTTPDownloader{
TargetFilePath: pluginUploadOption.pluginFilePath,
URL: pluginUploadOption.Remote,
UserName: pluginUploadOption.RemoteUser,
Password: pluginUploadOption.RemotePassword,
ShowProgress: true,
Debug: rootOptions.Debug,
}
if err := downloader.DownloadFile(); err != nil {
log.Fatal(err)
}
} else if len(args) == 0 {
path, _ := os.Getwd()
dirName := filepath.Base(path)
dirName = strings.Replace(dirName, "-plugin", "", -1)
path += fmt.Sprintf("/target/%s.hpi", dirName)
pluginUploadOption.pluginFilePath = path
} else {
pluginUploadOption.pluginFilePath = args[0]
}
},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
jenkins := getCurrentJenkins() jenkins := getCurrentJenkins()
jclient := &client.PluginManager{} jclient := &client.PluginManager{}
...@@ -21,7 +88,12 @@ var pluginUploadCmd = &cobra.Command{ ...@@ -21,7 +88,12 @@ var pluginUploadCmd = &cobra.Command{
jclient.Token = jenkins.Token jclient.Token = jenkins.Token
jclient.Proxy = jenkins.Proxy jclient.Proxy = jenkins.Proxy
jclient.ProxyAuth = jenkins.ProxyAuth jclient.ProxyAuth = jenkins.ProxyAuth
jclient.Debug = rootOptions.Debug
if pluginUploadOption.Remote != "" {
defer os.Remove(pluginUploadOption.pluginFilePath)
}
jclient.Upload() jclient.Upload(pluginUploadOption.pluginFilePath)
}, },
} }
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
type RootOptions struct { type RootOptions struct {
Version bool Version bool
Debug bool
} }
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
...@@ -48,6 +49,7 @@ var rootOptions RootOptions ...@@ -48,6 +49,7 @@ var rootOptions RootOptions
func init() { func init() {
cobra.OnInitialize(initConfig) cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().BoolVarP(&rootOptions.Version, "version", "v", false, "Print the version of Jenkins CLI") rootCmd.PersistentFlags().BoolVarP(&rootOptions.Version, "version", "v", false, "Print the version of Jenkins CLI")
rootCmd.PersistentFlags().BoolVarP(&rootOptions.Debug, "debug", "", false, "Print the output into debug.html")
} }
func initConfig() { func initConfig() {
......
...@@ -18,6 +18,8 @@ type JenkinsCore struct { ...@@ -18,6 +18,8 @@ type JenkinsCore struct {
Token string Token string
Proxy string Proxy string
ProxyAuth string ProxyAuth string
Debug bool
} }
type JenkinsCrumb struct { type JenkinsCrumb struct {
......
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/gosuri/uiprogress" "github.com/linuxsuren/jenkins-cli/util"
) )
type PluginManager struct { type PluginManager struct {
...@@ -220,15 +220,11 @@ func (p *PluginManager) UninstallPlugin(name string) (err error) { ...@@ -220,15 +220,11 @@ func (p *PluginManager) UninstallPlugin(name string) (err error) {
return return
} }
func (p *PluginManager) Upload() { // Upload will upload a file from local filesystem into Jenkins
func (p *PluginManager) Upload(pluginFile string) {
api := fmt.Sprintf("%s/pluginManager/uploadPlugin", p.URL) api := fmt.Sprintf("%s/pluginManager/uploadPlugin", p.URL)
path, _ := os.Getwd()
dirName := filepath.Base(path)
dirName = strings.Replace(dirName, "-plugin", "", -1)
path += fmt.Sprintf("/target/%s.hpi", dirName)
extraParams := map[string]string{} extraParams := map[string]string{}
request, err := newfileUploadRequest(api, extraParams, "@name", path) request, err := newfileUploadRequest(api, extraParams, "@name", pluginFile)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
...@@ -245,9 +241,10 @@ func (p *PluginManager) Upload() { ...@@ -245,9 +241,10 @@ func (p *PluginManager) Upload() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} else if response.StatusCode != 200 { } else if response.StatusCode != 200 {
fmt.Println("StatusCode", response.StatusCode)
var data []byte var data []byte
if data, err = ioutil.ReadAll(response.Body); err == nil { if data, err = ioutil.ReadAll(response.Body); err == nil && p.Debug {
fmt.Println(string(data)) ioutil.WriteFile("debug.html", data, 0664)
} else { } else {
log.Fatal(err) log.Fatal(err)
} }
...@@ -261,34 +258,6 @@ func (p *PluginManager) handleCheck(handle func(*http.Response)) func(*http.Resp ...@@ -261,34 +258,6 @@ func (p *PluginManager) handleCheck(handle func(*http.Response)) func(*http.Resp
return handle return handle
} }
type ProgressIndicator struct {
bytes.Buffer
Total float64
count float64
bar *uiprogress.Bar
}
func (i *ProgressIndicator) Init() {
uiprogress.Start() // start rendering
i.bar = uiprogress.AddBar(100) // Add a new bar
// optionally, append and prepend completion and elapsed time
i.bar.AppendCompleted()
// i.bar.PrependElapsed()
}
func (i *ProgressIndicator) Write(p []byte) (n int, err error) {
n, err = i.Buffer.Write(p)
return
}
func (i *ProgressIndicator) Read(p []byte) (n int, err error) {
n, err = i.Buffer.Read(p)
i.count += float64(n)
i.bar.Set((int)(i.count * 100 / i.Total))
return
}
func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) { func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
file, err := os.Open(path) file, err := os.Open(path)
if err != nil { if err != nil {
...@@ -303,12 +272,15 @@ func newfileUploadRequest(uri string, params map[string]string, paramName, path ...@@ -303,12 +272,15 @@ func newfileUploadRequest(uri string, params map[string]string, paramName, path
} }
defer file.Close() defer file.Close()
// body := &bytes.Buffer{} bytesBuffer := &bytes.Buffer{}
body := &ProgressIndicator{ progressWriter := &util.ProgressIndicator{
Total: total, Total: total,
Writer: bytesBuffer,
Reader: bytesBuffer,
Title: "Uploading",
} }
body.Init() progressWriter.Init()
writer := multipart.NewWriter(body) writer := multipart.NewWriter(bytesBuffer)
part, err := writer.CreateFormFile(paramName, filepath.Base(path)) part, err := writer.CreateFormFile(paramName, filepath.Base(path))
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -324,7 +296,7 @@ func newfileUploadRequest(uri string, params map[string]string, paramName, path ...@@ -324,7 +296,7 @@ func newfileUploadRequest(uri string, params map[string]string, paramName, path
return nil, err return nil, err
} }
req, err := http.NewRequest("POST", uri, body) req, err := http.NewRequest("POST", uri, progressWriter)
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
return req, err return req, err
} }
package util
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
"github.com/gosuri/uiprogress"
)
type HTTPDownloader struct {
TargetFilePath string
URL string
ShowProgress bool
UserName string
Password string
Debug bool
}
// DownloadFile download a file with the progress
func (h *HTTPDownloader) DownloadFile() error {
filepath, url, showProgress := h.TargetFilePath, h.URL, h.ShowProgress
// Get the data
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
if h.UserName != "" && h.Password != "" {
req.SetBasicAuth(h.UserName, h.Password)
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
if h.Debug {
if data, err := ioutil.ReadAll(resp.Body); err == nil {
ioutil.WriteFile("debug-download.html", data, 0664)
}
}
return fmt.Errorf("Invalidate status code: %d", resp.StatusCode)
}
writer := &ProgressIndicator{
Title: "Downloading",
}
if showProgress {
if total, ok := resp.Header["Content-Length"]; ok && len(total) > 0 {
fileLength, err := strconv.ParseInt(total[0], 10, 64)
if err == nil {
writer.Total = float64(fileLength)
}
}
}
// Create the file
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
writer.Writer = out
writer.Init()
// Write the body to file
_, err = io.Copy(writer, resp.Body)
return err
}
// ProgressIndicator hold the progress of io operation
type ProgressIndicator struct {
Writer io.Writer
Reader io.Reader
Title string
// bytes.Buffer
Total float64
count float64
bar *uiprogress.Bar
}
// Init set the default value for progress indicator
func (i *ProgressIndicator) Init() {
uiprogress.Start() // start rendering
i.bar = uiprogress.AddBar(100) // Add a new bar
// optionally, append and prepend completion and elapsed time
i.bar.AppendCompleted()
// i.bar.PrependElapsed()
if i.Title != "" {
i.bar.PrependFunc(func(b *uiprogress.Bar) string {
return fmt.Sprintf("%s: ", i.Title)
})
}
}
func (i *ProgressIndicator) Write(p []byte) (n int, err error) {
n, err = i.Writer.Write(p)
i.setBar(n)
return
}
func (i *ProgressIndicator) Read(p []byte) (n int, err error) {
n, err = i.Reader.Read(p)
i.setBar(n)
return
}
func (i *ProgressIndicator) setBar(n int) {
i.count += float64(n)
i.bar.Set((int)(i.count * 100 / i.Total))
}
...@@ -6,12 +6,6 @@ import ( ...@@ -6,12 +6,6 @@ import (
"unicode/utf8" "unicode/utf8"
) )
// const (
// ALIGN_LEFT = 0
// ALIGN_CENTER = 1
// ALIGN_RIGHT = 2
// )
type Table struct { type Table struct {
Out io.Writer Out io.Writer
Rows [][]string Rows [][]string
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册