Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add job trigger and get log ouput #771

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

bin/
bin/**/jcli
release/
jcli

Expand Down
12 changes: 12 additions & 0 deletions .jenkins-cli.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
current: test
jenkins_servers:
- name: yourServer
url: http://localhost:8080/jenkins
username: test
token: 211e3a2f0231198856dceaff96f2v75ce3
insecureSkipVerify: true
#mirrors:
#- name: default
# url: http://mirrors.jenkins.io/
# Language context is accept-language for HTTP header, It contains zh-CN/zh-TW/en/en-US/ja and so on
# Goto 'http://localhost:8080/jenkins/me/configure', then you can generate your token.
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
FROM golang:1.12 AS builder
FROM golang:1.16 AS builder

WORKDIR /work
COPY . .
RUN ls -hal && make linux

FROM alpine:3.10
ENV JOB_NAME "test"
COPY --from=builder /work/bin/linux/jcli /usr/bin/jcli
RUN jcli config generate -i=false > ~/.jenkins-cli.yaml
COPY bin/build.sh /usr/bin/jclih
RUN chmod +x /usr/bin/jclih

ENTRYPOINT ["jcli"]
ENTRYPOINT ["jclih"]
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ gen-data-darwin: go-bindata-download-darwin

verify: dep tools lint

pre-build: fmt vet
pre-build:
export GO111MODULE=on
export GOPROXY=https://goproxy.io
go mod tidy
Expand Down
62 changes: 45 additions & 17 deletions app/cmd/job_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"encoding/json"
"fmt"
"strings"
"time"

"github.com/jenkins-zh/jenkins-cli/app/cmd/common"
"github.com/jenkins-zh/jenkins-cli/app/i18n"
Expand All @@ -18,15 +19,18 @@
common.Option
cobra_ext.OutputOption

Param string
ParamArray []string
Param string
ParamArray []string
ParamJsonString string

Check failure on line 24 in app/cmd/job_build.go

View workflow job for this annotation

GitHub Actions / Lint

struct field ParamJsonString should be ParamJSONString

ParamFilePathArray []string

Wait bool
WaitTime int
Delay int
Cause string
Wait bool
WaitTime int
WaitInterval int
Delay int
Cause string
LogConsole bool
}

var jobBuildOption JobBuildOption
Expand All @@ -40,23 +44,28 @@
jobCmd.AddCommand(jobBuildCmd)
jobBuildCmd.Flags().BoolVarP(&jobBuildOption.Batch, "batch", "b", false, "Batch mode, no need confirm")
jobBuildCmd.Flags().StringVarP(&jobBuildOption.Param, "param", "", "",
i18n.T("Parameters of the job which is JSON format"))
i18n.T("Parameters of the job which is JSON format, for example: --param '{\"limit\":\"2\",\"timeoutLimit\":\"10\"}'"))
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"))
i18n.T("Parameters of the job which are the entry format, for example: --param-entry name1=value1, --param-entry name2=value2"))
jobBuildCmd.Flags().StringArrayVar(&jobBuildOption.ParamFilePathArray, "param-file", nil,
i18n.T("Parameters of the job which is file path, for example: --param-file name=filename"))
jobBuildCmd.Flags().BoolVarP(&jobBuildOption.Wait, "wait", "", false,
i18n.T("If you want to wait for the build ID from Jenkins. You need to install plugin pipeline-restful-api first"))
jobBuildCmd.Flags().IntVarP(&jobBuildOption.WaitTime, "wait-timeout", "", 30,
jobBuildCmd.Flags().IntVarP(&jobBuildOption.WaitTime, "wait-timeout", "", 60,
i18n.T("The timeout of seconds when you wait for the build ID"))
jobBuildCmd.Flags().IntVarP(&jobBuildOption.WaitInterval, "wait-interval", "", 10,
i18n.T("The interval of seconds when you want to wait for buildID... query, use with wait"))
jobBuildCmd.Flags().IntVarP(&jobBuildOption.Delay, "delay", "", 0,
i18n.T("Delay when trigger a Jenkins job"))
jobBuildCmd.Flags().StringVarP(&jobBuildOption.Cause, "cause", "", "triggered by jcli",
i18n.T("The cause of a job build"))
jobBuildCmd.Flags().BoolVarP(&jobBuildOption.LogConsole, "log", "l", false,
i18n.T("If you want to wait for build log and wait log output end"))

jobBuildOption.SetFlagWithHeaders(jobBuildCmd, "Number,URL")
jobBuildOption.BatchOption.Stdio = common.GetSystemStdio()
jobBuildOption.Option.Stdio = common.GetSystemStdio()

}

var jobBuildCmd = &cobra.Command{
Expand All @@ -66,15 +75,28 @@
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(_ *cobra.Command, _ []string) (err error) {
if jobBuildOption.ParamArray == nil && jobBuildOption.ParamFilePathArray == nil {
if jobBuildOption.ParamArray == nil && jobBuildOption.ParamFilePathArray == nil && jobBuildOption.Param == "" {
return
}

paramDefs := make([]client.ParameterDefinition, 0)
if jobBuildOption.Param != "" {
if err = json.Unmarshal([]byte(jobBuildOption.Param), &paramDefs); err != nil {
paramMap := make(map[string]interface{})
if err = json.Unmarshal([]byte(jobBuildOption.Param), &paramMap); err != nil {
logger.Error(fmt.Sprintf("build param unmarshal error %v", err.Error()))
return
}
for key, value := range paramMap {
if key == "" || value == nil {
logger.Error("build param key or value empty")
return
}
paramDefs = append(paramDefs, client.ParameterDefinition{
Name: key,
Value: fmt.Sprintf("%v", value),
Type: client.StringParameterDefinition,
})
}
}

for _, paramEntry := range jobBuildOption.ParamArray {
Expand Down Expand Up @@ -113,6 +135,7 @@
jclient := &client.JobClient{
JenkinsCore: client.JenkinsCore{
RoundTripper: jobBuildOption.RoundTripper,
Timeout: time.Duration(jobBuildOption.WaitTime) * time.Second,
},
}
getCurrentJenkinsAndClient(&(jclient.JenkinsCore))
Expand Down Expand Up @@ -150,13 +173,18 @@
}

if err == nil {
options := client.JobCmdOptionsCommon{
Wait: jobBuildOption.Wait,
WaitTime: jobBuildOption.WaitTime,
WaitInterval: jobBuildOption.WaitInterval,
LogConsole: jobBuildOption.LogConsole,
}

if hasParam {
err = jclient.BuildWithParams(name, paramDefs)
} else if jobBuildOption.Wait {
var build client.IdentityBuild
if build, err = jclient.BuildAndReturn(name, jobBuildOption.Cause, jobBuildOption.WaitTime, jobBuildOption.Delay); err == nil {
jobBuildOption.Writer = cmd.OutOrStdout()
err = jobBuildOption.OutputV2([1]client.SimpleJobBuild{build.Build.SimpleJobBuild})
var jobState client.JenkinsBuildState
jobState, err = jclient.BuildWithParamsGetResponse(name, paramDefs, options)
if err == nil && jobBuildOption.LogConsole && jobState.RunId > 0 {
err = printLogRunFunc(name, JobLogOptionGetDefault(int(jobState.RunId)), cmd)
}
} else {
err = jclient.Build(name)
Expand Down
116 changes: 81 additions & 35 deletions app/cmd/job_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,31 @@
LastBuildID int
LastBuildURL string
NumberOfLines int
ExitCode bool

RoundTripper http.RoundTripper
}

var jobLogOption JobLogOption

const (
LogFinishMsg = "finish end the job logs"

Check failure on line 32 in app/cmd/job_log.go

View workflow job for this annotation

GitHub Actions / Lint

exported const LogFinishMsg should have comment (or a comment on this block) or be unexported
JobResultSuccess = "SUCCESS"
JobResultFailed = "FAILURE"
)

func init() {
jobCmd.AddCommand(jobLogCmd)
jobLogCmd.Flags().IntVarP(&jobLogOption.History, "history", "s", -1,
i18n.T("Specific build history of log"))
jobLogCmd.Flags().BoolVarP(&jobLogOption.Watch, "watch", "w", false,
i18n.T("Watch the job logs"))
jobLogCmd.Flags().IntVarP(&jobLogOption.Interval, "interval", "i", 1,
i18n.T("Interval of watch"))
jobLogCmd.Flags().IntVarP(&jobLogOption.Interval, "interval", "i", 10,
i18n.T("Interval of watch seconds"))
jobLogCmd.Flags().IntVarP(&jobLogOption.NumberOfLines, "tail", "t", -1,
i18n.T("The last number of lines of the log"))
jobLogCmd.Flags().BoolVarP(&jobLogOption.ExitCode, "exit-code", "e", false,
i18n.T("Watch the job logs with job state, failed exit 1"))
}

var jobLogCmd = &cobra.Command{
Expand All @@ -48,6 +57,7 @@
Example: `jcli job log <jobName> [buildID]
jcli job log <jobName> --history 1
jcli job log <jobName> --watch
jcli job log <jobName> --watch --exit-code
jcli job log <jobName> --tail <numberOfLines>`,
PreRunE: func(_ *cobra.Command, args []string) (err error) {
if len(args) >= 3 && jobLogOption.History == -1 {
Expand All @@ -62,39 +72,9 @@
return
},
Run: func(cmd *cobra.Command, args []string) {
name := args[0]

jclient := &client.JobClient{
JenkinsCore: client.JenkinsCore{
RoundTripper: jobLogOption.RoundTripper,
},
}
getCurrentJenkinsAndClientOrDie(&(jclient.JenkinsCore))

lastBuildID := -1
var jobBuild *client.JobBuild
var err error
for {
if jobBuild, err = jclient.GetBuild(name, jobLogOption.History); err == nil {
jobLogOption.LastBuildID = jobBuild.Number
jobLogOption.LastBuildURL = jobBuild.URL
} else {
break
}

if lastBuildID != jobLogOption.LastBuildID {
lastBuildID = jobLogOption.LastBuildID
cmd.Println("Current build number:", jobLogOption.LastBuildID)
cmd.Println("Current build url:", jobLogOption.LastBuildURL)

err = printLog(jclient, cmd, name, jobLogOption.History, 0, jobLogOption.NumberOfLines)
}

if err != nil || !jobLogOption.Watch {
break
}

time.Sleep(time.Duration(jobLogOption.Interval) * time.Second)
err := printLogRunFunc(args[0], jobLogOption, cmd)
if err != nil {
logger.Sugar().Infof("[ERR] print log func error %v", err.Error())
}
},
}
Expand Down Expand Up @@ -133,7 +113,73 @@

if jobLog.HasMore {
err = printLog(jclient, cmd, jobName, history, jobLog.NextStart, numberOfLines)
} else {
err = fmt.Errorf(LogFinishMsg)
}
}
return
}

func printLogRunFunc(jobName string, jobLogOption JobLogOption, cmd *cobra.Command) (err error) {
name := jobName

jclient := &client.JobClient{
JenkinsCore: client.JenkinsCore{
RoundTripper: jobLogOption.RoundTripper,
},
}
getCurrentJenkinsAndClientOrDie(&(jclient.JenkinsCore))

lastBuildID := -1
var jobBuild *client.JobBuild
for {
if jobBuild, err = jclient.GetBuild(name, jobLogOption.History); err == nil {
jobLogOption.LastBuildID = jobBuild.Number
jobLogOption.LastBuildURL = jobBuild.URL
} else {
logger.Sugar().Fatal("[ERR] get job build info failed...")
}

if lastBuildID != jobLogOption.LastBuildID {
lastBuildID = jobLogOption.LastBuildID
cmd.Println("[INFO] Current build number:", jobLogOption.LastBuildID)
cmd.Printf("[INFO] Current build url: %sconsole\n", jobLogOption.LastBuildURL)

err = printLog(jclient, cmd, name, jobLogOption.History, 0, jobLogOption.NumberOfLines)
}

if err != nil || !jobLogOption.Watch {
if err.Error() == LogFinishMsg {
err = nil
cmd.Println("[INFO] current log finish output... exit")
if jobLogOption.ExitCode {
if jobBuild, err = jclient.GetBuild(name, jobLogOption.History); err == nil {
if jobBuild.Result == JobResultFailed {
logger.Sugar().Fatalf("[ERR] job build %v end with build state failed [%vconsole], exit...", jobLogOption.LastBuildID, jobLogOption.LastBuildURL)
}
} else {
logger.Sugar().Fatal("[ERR] get job build info failed...")
}
}
}
break
}

time.Sleep(time.Duration(jobLogOption.Interval) * time.Second)
}
return
}

// JobLogOptionGetDefault get default config for job log
func JobLogOptionGetDefault(runId int) JobLogOption {

Check failure on line 174 in app/cmd/job_log.go

View workflow job for this annotation

GitHub Actions / Lint

func parameter runId should be runID
return JobLogOption{
History: runId,
WatchOption: common.WatchOption{
Watch: true,
Interval: jobBuildOption.WaitInterval,
Count: 9999,
},
NumberOfLines: 9999,
ExitCode: true,
}
}
15 changes: 15 additions & 0 deletions bin/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/sh

set -e

BIN_PATH=/usr/bin/jcli

# If we run make directly, any files created on the bind mount
# will have awkward ownership. So we switch to a user with the
# same user and group IDs as source directory. We have to set a
# few things up so that sudo works without complaining later on.
${BIN_PATH} job build ${JOB_NAME} \
-b --url https://xxxx.com \
--config-load false \
--wait -l \
--logger-level info $*
Loading
Loading