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

Add bash completion support to plugin, config and job cmd (#242)

* Add bash completion support to plugin upgrade

* Change the function name

* Add support to completion to open jenkins

* Add comment on exported struct

* Add support to completion for job name

* Allow job name support URL path

* Fix cannot deletee a job which located a folder

* Fix the unit test error
上级 3eb9b5ec
......@@ -13,6 +13,8 @@ import (
// OutputOption represent the format of output
type OutputOption struct {
Format string
WithoutHeaders bool
}
// FormatOutput is the interface of format output
......@@ -43,18 +45,9 @@ func (o *OutputOption) Output(obj interface{}) (data []byte, err error) {
// SetFlag set flag of output format
func (o *OutputOption) SetFlag(cmd *cobra.Command) {
cmd.Flags().StringVarP(&o.Format, "output", "o", "table", "Format the output, supported formats: table, json, yaml")
}
// Format format the object into byte array
func Format(obj interface{}, format string) (data []byte, err error) {
if format == JSONOutputFormat {
return json.MarshalIndent(obj, "", " ")
} else if format == YAMLOutputFormat {
return yaml.Marshal(obj)
}
return nil, fmt.Errorf("not support format %s", format)
cmd.Flags().StringVarP(&o.Format, "output", "o", TableOutputFormat, "Format the output, supported formats: table, json, yaml")
cmd.Flags().BoolVarP(&o.WithoutHeaders, "no-headers", "", false,
`When using the default output format, don't print headers (default print headers)`)
}
// BatchOption represent the options for a batch operation
......
......@@ -4,12 +4,22 @@ import (
"bytes"
"fmt"
"github.com/jenkins-zh/jenkins-cli/app/helper"
"github.com/jenkins-zh/jenkins-cli/util"
"github.com/spf13/cobra"
)
// ConfigListOption option for config list command
type ConfigListOption struct {
OutputOption
}
var configListOption ConfigListOption
func init() {
configCmd.AddCommand(configListCmd)
configListOption.SetFlag(configListCmd)
}
var configListCmd = &cobra.Command{
......@@ -19,9 +29,20 @@ var configListCmd = &cobra.Command{
Run: func(cmd *cobra.Command, _ []string) {
current := getCurrentJenkins()
data, err := configListOption.Output(current)
cmd.Print(string(data))
helper.CheckErr(cmd, err)
},
}
// Output render data into byte array as a table format
func (o *ConfigListOption) Output(obj interface{}) (data []byte, err error) {
if data, err = o.OutputOption.Output(obj); err != nil && o.Format == TableOutputFormat {
current := obj.(*JenkinsServer)
buf := new(bytes.Buffer)
table := util.CreateTable(buf)
table.AddRow("number", "name", "url", "description")
table := util.CreateTableWithHeader(buf, o.WithoutHeaders)
table.AddHeader("number", "name", "url", "description")
for i, jenkins := range getConfig().JenkinsServers {
name := jenkins.Name
if name == current.Name {
......@@ -33,6 +54,8 @@ var configListCmd = &cobra.Command{
table.AddRow(fmt.Sprintf("%d", i), name, jenkins.URL, jenkins.Description)
}
table.Render()
cmd.Print(string(buf.Bytes()))
},
err = nil
data = buf.Bytes()
}
return
}
......@@ -26,7 +26,8 @@ func init() {
jobCmd.AddCommand(jobSearchCmd)
jobSearchCmd.Flags().IntVarP(&jobSearchOption.Max, "max", "", 10, "The number of limitation to print")
jobSearchCmd.Flags().BoolVarP(&jobSearchOption.PrintAll, "all", "", false, "Print all items if there's no keyword")
jobSearchCmd.Flags().StringVarP(&jobSearchOption.Format, "output", "o", "json", "Format the output")
jobSearchCmd.Flags().StringVarP(&jobSearchOption.Format, "output", "o", "json",
`Formats of the output which contain name, path`)
}
var jobSearchCmd = &cobra.Command{
......@@ -66,15 +67,40 @@ var jobSearchCmd = &cobra.Command{
// Output render data into byte array
func (o *JobSearchOption) Output(obj interface{}) (data []byte, err error) {
if data, err = o.OutputOption.Output(obj); err != nil && o.OutputOption.Format == "name" {
if data, err = o.OutputOption.Output(obj); err != nil {
var formatFunc JobNameFormat
switch o.OutputOption.Format {
case "name":
formatFunc = simpleFormat
case "path":
formatFunc = pathFormat
}
if formatFunc == nil {
err = fmt.Errorf("unknow format %s", o.OutputOption.Format)
return
}
buf := ""
searchResult := obj.(*client.SearchResult)
for _, item := range searchResult.Suggestions {
buf = fmt.Sprintf("%s%s\n", buf, item.Name)
buf = fmt.Sprintf("%s%s\n", buf, formatFunc(item.Name))
}
data = []byte(strings.Trim(buf, "\n"))
err = nil
}
return
}
// JobNameFormat format the job name
type JobNameFormat func(string) string
func simpleFormat(name string) string {
return name
}
func pathFormat(name string) string {
return client.ParseJobPath(name)
}
......@@ -22,29 +22,37 @@ var openOption OpenOption
func init() {
rootCmd.AddCommand(openCmd)
openCmd.PersistentFlags().StringVarP(&openOption.Name, "name", "n", "", "Open a specific Jenkins by name")
openCmd.PersistentFlags().BoolVarP(&openOption.Config, "config", "c", false, "Open the configuration page of Jenkins")
openCmd.Flags().StringVarP(&openOption.Name, "name", "n", "", "Open a specific Jenkins by name")
openCmd.Flags().BoolVarP(&openOption.Config, "config", "c", false, "Open the configuration page of Jenkins")
openOption.SetFlag(openCmd)
}
var openCmd = &cobra.Command{
Use: "open",
Short: "Open your Jenkins with a browse",
Long: `Open your Jenkins with a browse`,
Run: func(_ *cobra.Command, _ []string) {
Use: "open [config name]",
Short: "Open your Jenkins with a browse",
Long: `Open your Jenkins with a browse`,
Example: `jcli open -n <config name>`,
Run: func(_ *cobra.Command, args []string) {
var jenkins *JenkinsServer
if openOption.Name == "" && openOption.Interactive {
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: "Choose a Jenkins that you want to open:",
Options: jenkinsNames,
}
survey.AskOne(prompt, &(openOption.Name))
survey.AskOne(prompt, &(configName))
}
if openOption.Name != "" {
jenkins = findJenkinsByName(openOption.Name)
if configName != "" {
jenkins = findJenkinsByName(configName)
} else {
jenkins = getCurrentJenkins()
}
......@@ -56,7 +64,7 @@ var openCmd = &cobra.Command{
}
open(url)
} else {
log.Fatalf("No URL found with Jenkins %s", openOption.Name)
log.Fatalf("No URL found with Jenkins %s", configName)
}
},
}
......
......@@ -34,7 +34,8 @@ var pluginListCmd = &cobra.Command{
Short: "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`,
jcli plugin list --filter hasUpdate
jcli plugin list --no-headers`,
Run: func(cmd *cobra.Command, _ []string) {
jclient := &client.PluginManager{
JenkinsCore: client.JenkinsCore{
......@@ -112,12 +113,12 @@ var pluginListCmd = &cobra.Command{
// Output render data into byte array as a table format
func (o *PluginListOption) Output(obj interface{}) (data []byte, err error) {
if data, err = o.OutputOption.Output(obj); err != nil {
if data, err = o.OutputOption.Output(obj); err != nil && o.Format == TableOutputFormat {
buf := new(bytes.Buffer)
pluginList := obj.([]client.InstalledPlugin)
table := util.CreateTable(buf)
table.AddRow("number", "name", "version", "update")
table := util.CreateTableWithHeader(buf, o.WithoutHeaders)
table.AddHeader("number", "name", "version", "update")
for i, plugin := range pluginList {
table.AddRow(fmt.Sprintf("%d", i), plugin.ShortName, plugin.Version, fmt.Sprintf("%v", plugin.HasUpdate))
}
......
......@@ -76,6 +76,26 @@ var _ = Describe("plugin list command", func() {
`))
})
It("one plugin in the list without headers", func() {
data, err := generateSampleConfig()
Expect(err).To(BeNil())
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
Expect(err).To(BeNil())
request, _ := client.PrepareForOneInstalledPlugin(roundTripper, "http://localhost:8080/jenkins")
request.SetBasicAuth("admin", "111e3a2f0231198855dceaff96f20540a9")
rootCmd.SetArgs([]string{"plugin", "list", "fake", "--no-headers"})
buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal(`0 fake 1.0 true
`))
})
It("one plugin output with json format", func() {
data, err := generateSampleConfig()
Expect(err).To(BeNil())
......@@ -94,6 +114,44 @@ var _ = Describe("plugin list command", func() {
Expect(buf.String()).To(Equal(pluginsJSON()))
})
It("one plugin output with yaml format", func() {
data, err := generateSampleConfig()
Expect(err).To(BeNil())
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
Expect(err).To(BeNil())
request, _ := client.PrepareForOneInstalledPlugin(roundTripper, "http://localhost:8080/jenkins")
request.SetBasicAuth("admin", "111e3a2f0231198855dceaff96f20540a9")
rootCmd.SetArgs([]string{"plugin", "list", "fake", "--output", "yaml", "--filter", "hasUpdate", "--filter", "name=fake", "--filter", "enable", "--filter", "active"})
buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal(pluginYaml()))
})
It("one plugin output with not support format", func() {
data, err := generateSampleConfig()
Expect(err).To(BeNil())
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
Expect(err).To(BeNil())
request, _ := client.PrepareForOneInstalledPlugin(roundTripper, "http://localhost:8080/jenkins")
request.SetBasicAuth("admin", "111e3a2f0231198855dceaff96f20540a9")
rootCmd.SetArgs([]string{"plugin", "list", "fake", "--output", "fake"})
buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal("error: not support format fake"))
})
})
})
......@@ -120,3 +178,25 @@ func pluginsJSON() string {
}
]`
}
func pluginYaml() string {
return `- plugin:
active: true
enabled: false
bundled: false
downgradable: false
deleted: false
enable: true
shortname: fake
longname: ""
version: "1.0"
url: ""
hasupdate: true
pinned: false
requiredcorevesion: ""
minimumjavaversion: ""
supportdynamicload: ""
backversion: ""
dependencies: []
`
}
......@@ -24,7 +24,7 @@ var pluginSearchOption PluginSearchOption
func init() {
pluginCmd.AddCommand(pluginSearchCmd)
pluginSearchCmd.PersistentFlags().StringVarP(&pluginSearchOption.Format, "output", "o", TableOutputFormat, "Format the output")
pluginSearchOption.SetFlag(pluginSearchCmd)
}
var pluginSearchCmd = &cobra.Command{
......@@ -168,8 +168,8 @@ func (o *PluginSearchOption) Output(obj interface{}) (data []byte, err error) {
buf := new(bytes.Buffer)
if len(pluginList) != 0 {
table := util.CreateTable(buf)
table.AddRow("number", "name", "installed", "version", "installedVersion", "title")
table := util.CreateTableWithHeader(buf, pluginSearchOption.WithoutHeaders)
table.AddHeader("number", "name", "installed", "version", "installedVersion", "title")
for i, plugin := range pluginList {
formatTable(&table, i, plugin)
......
......@@ -27,9 +27,10 @@ func init() {
}
var pluginUpgradeCmd = &cobra.Command{
Use: "upgrade [plugin name]",
Short: "Upgrade the specific plugin",
Long: `Upgrade the specific plugin`,
Use: "upgrade [plugin name]",
Short: "Upgrade the specific plugin",
Long: `Upgrade the specific plugin`,
Example: `jcli plugin upgrade [tab][tab]`,
Run: func(cmd *cobra.Command, args []string) {
jclient := &client.PluginManager{
JenkinsCore: client.JenkinsCore{
......
package cmd
import (
"bytes"
"fmt"
"github.com/jenkins-zh/jenkins-cli/util"
"net/http"
"github.com/jenkins-zh/jenkins-cli/app/helper"
......@@ -20,7 +23,7 @@ var queueListOption QueueListOption
func init() {
queueCmd.AddCommand(queueListCmd)
queueListCmd.Flags().StringVarP(&queueListOption.Format, "output", "o", "json", "Format the output")
queueListOption.SetFlag(queueListCmd)
}
var queueListCmd = &cobra.Command{
......@@ -36,14 +39,32 @@ var queueListCmd = &cobra.Command{
}
getCurrentJenkinsAndClient(&(jclient.JenkinsCore))
status, err := jclient.Get()
if err == nil {
var err error
var jobQueue *client.JobQueue
if jobQueue, err = jclient.Get(); err == nil {
var data []byte
data, err = Format(status, queueListOption.Format)
if err == nil {
cmd.Printf("%s\n", string(data))
if data, err = queueListOption.Output(jobQueue); err == nil && len(data) > 0 {
cmd.Print(string(data))
}
}
helper.CheckErr(cmd, err)
},
}
// Output render data into byte array as a table format
func (o *QueueListOption) Output(obj interface{}) (data []byte, err error) {
if data, err = o.OutputOption.Output(obj); err != nil && o.Format == TableOutputFormat {
buf := new(bytes.Buffer)
jobQueue := obj.(*client.JobQueue)
table := util.CreateTableWithHeader(buf, o.WithoutHeaders)
table.AddHeader("number", "id", "why", "url")
for i, item := range jobQueue.Items {
table.AddRow(fmt.Sprintf("%d", i), fmt.Sprintf("%d", item.ID), item.Why, item.URL)
}
table.Render()
err = nil
data = buf.Bytes()
}
return
}
......@@ -47,7 +47,7 @@ var _ = Describe("queue list command", func() {
client.PrepareGetQueue(roundTripper, "http://localhost:8080/jenkins", "admin", "111e3a2f0231198855dceaff96f20540a9")
rootCmd.SetArgs([]string{"queue", "list"})
rootCmd.SetArgs([]string{"queue", "list", "-o", "json"})
buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)
......@@ -70,7 +70,26 @@ var _ = Describe("queue list command", func() {
"Actions": []
}
]
}
}`))
})
It("output with table format", func() {
data, err := generateSampleConfig()
Expect(err).To(BeNil())
err = ioutil.WriteFile(rootOptions.ConfigFile, data, 0664)
Expect(err).To(BeNil())
client.PrepareGetQueue(roundTripper, "http://localhost:8080/jenkins", "admin", "111e3a2f0231198855dceaff96f20540a9")
rootCmd.SetArgs([]string{"queue", "list", "-o", "table"})
buf := new(bytes.Buffer)
rootCmd.SetOutput(buf)
_, err = rootCmd.ExecuteC()
Expect(err).To(BeNil())
Expect(buf.String()).To(Equal(`number id why url
0 62 等待下一个可用的执行器 queue/item/62/
`))
})
})
......
......@@ -2,13 +2,14 @@ package cmd
import (
"fmt"
"github.com/jenkins-zh/jenkins-cli/util"
"io"
"log"
"os"
"os/exec"
"strings"
"github.com/jenkins-zh/jenkins-cli/util"
"github.com/jenkins-zh/jenkins-cli/client"
"github.com/jenkins-zh/jenkins-cli/app"
......@@ -34,6 +35,7 @@ var rootCmd = &cobra.Command{
Long: `jcli is Jenkins CLI which could help with your multiple Jenkins,
Manage your Jenkins and your pipelines
More information could found at https://jenkins-zh.cn`,
BashCompletionFunction: jcliBashCompletionFunc,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
var err error
if logger, err = util.InitLogger(rootOptions.LoggerLevel); err != nil {
......@@ -77,6 +79,7 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&rootOptions.Debug, "debug", "", false, "Print the output into debug.html")
rootCmd.PersistentFlags().StringVarP(&rootOptions.LoggerLevel, "logger-level", "", "warn",
"Logger level which could be: debug, info, warn, error")
rootCmd.SetOut(os.Stdout)
}
func initConfig() {
......@@ -187,3 +190,76 @@ func execute(command string, writer io.Writer) (err error) {
err = cmd.Run()
return
}
const (
jcliBashCompletionFunc = `__plugin_name_parse_get()
{
local jcli_output out
if jcli_output=$(jcli plugin list --filter hasUpdate --no-headers --filter name="$1" 2>/dev/null); then
out=($(echo "${jcli_output}" | awk '{print $2}'))
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__jcli_get_plugin_name()
{
__plugin_name_parse_get
if [[ $? -eq 0 ]]; then
return 0
fi
}
__config_name_parse_get()
{
local jcli_output out
if jcli_output=$(jcli config list --no-headers 2>/dev/null); then
out=($(echo "${jcli_output}" | awk '{print $2}'))
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__jcli_get_config_name()
{
__config_name_parse_get
if [[ $? -eq 0 ]]; then
return 0
fi
}
__job_name_parse_get()
{
local jcli_output out
if jcli_output=$(jcli job search -o path "$cur" 2>/dev/null); then
out=($(echo "${jcli_output}"))
COMPREPLY=( ${out} )
fi
}
__jcli_get_job_name()
{
__job_name_parse_get
if [[ $? -eq 0 ]]; then
return 0
fi
}
__jcli_custom_func() {
case ${last_command} in
jcli_plugin_upgrade | jcli_plugin_uninstall)
__jcli_get_plugin_name
return
;;
jcli_open)
__jcli_get_config_name
return
;;
jcli_job_build | jcli_job_stop | jcli_job_log | jcli_job_delete | jcli_job_history | jcli_job_artifact | jcli_job_input)
__jcli_get_job_name
return
;;
*)
;;
esac
}
`
)
......@@ -20,7 +20,7 @@ type ArtifactClient struct {
// List get the list of artifacts from a build
func (q *ArtifactClient) List(jobName string, buildID int) (artifacts []Artifact, err error) {
path := parseJobPath(jobName)
path := ParseJobPath(jobName)
var api string
if buildID < 1 {
api = fmt.Sprintf("%s/lastBuild/wfapi/artifacts", path)
......
......@@ -12,7 +12,7 @@ import (
// PrepareGetArtifacts only for test
func PrepareGetArtifacts(roundTripper *mhttp.MockRoundTripper, rootURL, user, passwd,
jobName string, buildID int) (response *http.Response) {
path := parseJobPath(jobName)
path := ParseJobPath(jobName)
var api string
if buildID <= 0 {
api = fmt.Sprintf("%s/lastBuild/wfapi/artifacts", path)
......
......@@ -25,14 +25,14 @@ func (q *JobClient) Search(keyword string, max int) (status *SearchResult, err e
// Build trigger a job
func (q *JobClient) Build(jobName string) (err error) {
path := parseJobPath(jobName)
path := ParseJobPath(jobName)
_, err = q.RequestWithoutData("POST", fmt.Sprintf("%s/build", path), nil, nil, 201)
return
}
// GetBuild get build information of a job
func (q *JobClient) GetBuild(jobName string, id int) (job *JobBuild, err error) {
path := parseJobPath(jobName)
path := ParseJobPath(jobName)
var api string
if id == -1 {
api = fmt.Sprintf("%s/lastBuild/api/json", path)
......@@ -46,7 +46,7 @@ func (q *JobClient) GetBuild(jobName string, id int) (job *JobBuild, err error)
// BuildWithParams build a job which has params
func (q *JobClient) BuildWithParams(jobName string, parameters []ParameterDefinition) (err error) {
path := parseJobPath(jobName)
path := ParseJobPath(jobName)
api := fmt.Sprintf("%s/build", path)
var paramJSON []byte
......@@ -68,7 +68,7 @@ func (q *JobClient) BuildWithParams(jobName string, parameters []ParameterDefini
// StopJob stops a job build
func (q *JobClient) StopJob(jobName string, num int) (err error) {
path := parseJobPath(jobName)
path := ParseJobPath(jobName)
api := fmt.Sprintf("%s/%d/stop", path, num)
_, err = q.RequestWithoutData("POST", api, nil, nil, 200)
......@@ -77,7 +77,7 @@ func (q *JobClient) StopJob(jobName string, num int) (err error) {
// GetJob returns the job info
func (q *JobClient) GetJob(name string) (job *Job, err error) {
path := parseJobPath(name)
path := ParseJobPath(name)
api := fmt.Sprintf("%s/api/json", path)
err = q.RequestWithData("GET", api, nil, nil, 200, &job)
......@@ -108,7 +108,7 @@ func (q *JobClient) GetJobTypeCategories() (jobCategories []JobCategory, err err
// GetPipeline return the pipeline object
func (q *JobClient) GetPipeline(name string) (pipeline *Pipeline, err error) {
path := parseJobPath(name)
path := ParseJobPath(name)
api := fmt.Sprintf("%s/restFul", path)
err = q.RequestWithData("GET", api, nil, nil, 200, &pipeline)
return
......@@ -119,7 +119,7 @@ func (q *JobClient) UpdatePipeline(name, script string) (err error) {
formData := url.Values{}
formData.Add("script", script)
path := parseJobPath(name)
path := ParseJobPath(name)
api := fmt.Sprintf("%s/restFul/update?%s", path, formData.Encode())
_, err = q.RequestWithoutData("POST", api, nil, nil, 200)
......@@ -146,7 +146,7 @@ func (q *JobClient) GetHistory(name string) (builds []*JobBuild, err error) {
// Log get the log of a job
func (q *JobClient) Log(jobName string, history int, start int64) (jobLog JobLog, err error) {
path := parseJobPath(jobName)
path := ParseJobPath(jobName)
var api string
if history == -1 {
api = fmt.Sprintf("%s%s/lastBuild/logText/progressiveText?start=%d", q.URL, path, start)
......@@ -221,7 +221,8 @@ func (q *JobClient) Delete(jobName string) (err error) {
statusCode int
)
api := fmt.Sprintf("/job/%s/doDelete", jobName)
jobName = ParseJobPath(jobName)
api := fmt.Sprintf("%s/doDelete", jobName)
header := map[string]string{
util.ContentType: util.ApplicationForm,
}
......@@ -236,7 +237,7 @@ func (q *JobClient) Delete(jobName string) (err error) {
// GetJobInputActions returns the all pending actions
func (q *JobClient) GetJobInputActions(jobName string, buildID int) (actions []JobInputItem, err error) {
path := parseJobPath(jobName)
path := ParseJobPath(jobName)
err = q.RequestWithData("GET", fmt.Sprintf("%s/%d/wfapi/pendingInputActions", path, buildID), nil, nil, 200, &actions)
return
}
......@@ -248,7 +249,7 @@ type JenkinsInputParametersRequest struct {
// JobInputSubmit submit the pending input request
func (q *JobClient) JobInputSubmit(jobName, inputID string, buildID int, abort bool, params map[string]string) (err error) {
jobPath := parseJobPath(jobName)
jobPath := ParseJobPath(jobName)
var api string
if abort {
api = fmt.Sprintf("%s/%d/input/%s/abort", jobPath, buildID, inputID)
......@@ -275,8 +276,13 @@ func (q *JobClient) JobInputSubmit(jobName, inputID string, buildID int, abort b
return
}
// parseJobPath leads with slash
func parseJobPath(jobName string) (path string) {
// ParseJobPath leads with slash
func ParseJobPath(jobName string) (path string) {
path = jobName
if jobName == "" || strings.HasPrefix(jobName, "/job") {
return
}
jobItems := strings.Split(jobName, " ")
path = ""
for _, item := range jobItems {
......
......@@ -392,3 +392,48 @@ var _ = Describe("job test", func() {
})
})
})
var _ = Describe("test function ParseJobPath", func() {
var (
path string
jobName string
)
JustBeforeEach(func() {
path = ParseJobPath(jobName)
})
It("job name is empty", func() {
Expect(path).To(BeEmpty())
})
Context("job name is not empty", func() {
BeforeEach(func() {
jobName = "abc"
})
It("job name separate with whitespaces", func() {
Expect(path).To(Equal(fmt.Sprintf("/job/%s", jobName)))
})
Context("multi level of job name", func() {
BeforeEach(func() {
jobName = "abc def"
})
It("should success", func() {
Expect(path).To(Equal("/job/abc/job/def"))
})
})
})
Context("job name with URL path", func() {
BeforeEach(func() {
jobName = "/job/abc/job/def"
})
It("should success", func() {
Expect(path).To(Equal(jobName))
})
})
})
......@@ -13,6 +13,8 @@ type Table struct {
ColumnWidths []int
ColumnAlign []int
Separator string
WithHeader bool
}
// CreateTable init a table object
......@@ -23,6 +25,13 @@ func CreateTable(out io.Writer) Table {
}
}
// CreateTableWithHeader init a table object
func CreateTableWithHeader(out io.Writer, withoutHeader bool) (table Table) {
table = CreateTable(out)
table.WithHeader = !withoutHeader
return
}
// Clear removes all rows while preserving the layout
func (t *Table) Clear() {
t.Rows = [][]string{}
......@@ -33,6 +42,14 @@ func (t *Table) AddRow(col ...string) {
t.Rows = append(t.Rows, col)
}
// AddHeader adds a header to the table
func (t *Table) AddHeader(col ...string) {
if !t.WithHeader {
return
}
t.AddRow(col...)
}
// Render render the table into byte array
func (t *Table) Render() {
// lets figure out the max widths of each column
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册