Skip to content

Commit

Permalink
feat: add job build and wait log with exit code
Browse files Browse the repository at this point in the history
add job build and wait log with exit code

Log:
Change-Id: I8ae90c8649cbbb817e339119f75db54caa6fd0e3
  • Loading branch information
guofei committed Jul 5, 2024
1 parent bc17586 commit a50476f
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 27 deletions.
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"]
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 @@ import (
"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 @@ type JobBuildOption struct {
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 @@ func init() {
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 @@ var jobBuildCmd = &cobra.Command{
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 @@ You need to give the parameters if your pipeline has them. Learn more about it f
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 @@ You need to give the parameters if your pipeline has them. Learn more about it f
}

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
20 changes: 17 additions & 3 deletions app/cmd/job_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,16 @@ func printLogRunFunc(jobName string, jobLogOption JobLogOption, cmd *cobra.Comma

if lastBuildID != jobLogOption.LastBuildID {
lastBuildID = jobLogOption.LastBuildID
cmd.Println("Current build number:", jobLogOption.LastBuildID)
cmd.Println("Current build url:", jobLogOption.LastBuildURL)
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")
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 {
Expand All @@ -169,3 +169,17 @@ func printLogRunFunc(jobName string, jobLogOption JobLogOption, cmd *cobra.Comma
}
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 $*
42 changes: 40 additions & 2 deletions client/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"go.uber.org/zap"
"io"
"io/ioutil"
"log"
"moul.io/http2curl"
"net/http"
"strconv"
"time"

"go.uber.org/zap"
"moul.io/http2curl"

"github.com/jenkins-zh/jenkins-cli/util"
ext "github.com/linuxsuren/cobra-extension/version"
httpdownloader "github.com/linuxsuren/http-downloader/pkg"
Expand Down Expand Up @@ -167,9 +169,45 @@ func (j *JenkinsCore) RequestWithoutData(method, api string, headers map[string]
statusCode != successCode {
err = j.ErrorHandle(statusCode, data)
}

return
}

// RequestWithoutData requests the api without handling data
func (j *JenkinsCore) RequestWithDataResponse(method, api string, headers map[string]string,
payload io.Reader, successCode int) (JenkinsBuildState, error) {
var (
data []byte
state JenkinsBuildState
)

if response, err := j.RequestWithResponse(method, api, headers, payload); err == nil {
statusCode := response.StatusCode
data, _ = ioutil.ReadAll(response.Body)
if statusCode == successCode {
state.BodyData = data
state.StatusCode = response.StatusCode
if len(response.Header.Get("Location")) > 0 {
locationSlice := util.ArraySplitAndDeleteEmpty(response.Header.Get("Location"), "/")
queueId := locationSlice[len(locationSlice)-1]
if len(queueId) > 0 {
if state.QueueId, err = strconv.ParseInt(queueId, 10, 64); err != nil {
logger.Error("request job run queue error", zap.String("queue id", queueId))
return state, err
}
}
}
} else {
err = j.ErrorHandle(statusCode, data)
return state, err
}
} else {
return state, err
}

return state, nil
}

// ErrorHandle handles the error cases
func (j *JenkinsCore) ErrorHandle(statusCode int, data []byte) (err error) {
if statusCode >= 400 && statusCode < 500 {
Expand Down
Loading

0 comments on commit a50476f

Please sign in to comment.