Skip to content

Commit

Permalink
add more function (#9)
Browse files Browse the repository at this point in the history
* add doc
  • Loading branch information
joelee2012 authored Feb 28, 2022
1 parent b865ab1 commit bd9d63c
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 35 deletions.
60 changes: 41 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,68 @@
![GitHub](https://img.shields.io/github/license/joelee2012/go-jenkins)

# go-jenkins
[Golang](https://golang.org/) client library for [Jenkins API](https://wiki.jenkins.io/display/JENKINS/Remote+access+API).
> Ported from [api4jenkins](https://github.com/joelee2012/api4jenkins>), a [Python3](https://www.python.org/) client library for [Jenkins API](https://wiki.jenkins.io/display/JENKINS/Remote+access+API).
[Golang](https://golang.org/) client library for accessing [Jenkins API](https://www.jenkins.io/doc/book/using/remote-access-api/).
> Ported from [api4jenkins](https://github.com/joelee2012/api4jenkins>), a [Python3](https://www.python.org/) client library for [Jenkins API](https://www.jenkins.io/doc/book/using/remote-access-api/).
# Features
This API client package covers most of the existing Jenkins API calls and is updated regularly to add new and/or missing endpoints.

Currently, the following are supported:

- Job
- Build
- Credential
- View
- Queue
- Node

# Usage

```go
import "github.com/joelee2012/go-jenkins"
```

## Example
```go
package main

import (
"log"
"time"

"github.com/joelee2012/go-jenkins/jenkins"
"github.com/joelee2012/go-jenkins"
)

func main() {
// Construct new client
client, err := jenkins.NewClient("http://localhost:8080/", "admin", "1234")
if err != nil {
log.Fatalln(err)
}
xml := `<?xml version='1.1' encoding='UTF-8'?>
<flow-definition plugin="workflow-job">
<definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps">
<definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps">
<script>#!groovy
pipeline {
agent any
stages {
stage('build'){
steps{
sh 'echo $JENKINS_VERSION'
}
}
}
}</script>
pipeline {
agent any
stages {
stage('build'){
steps{
sh 'echo $JENKINS_VERSION'
}
}
}
}</script>
<sandbox>true</sandbox>
</definition>
<disabled>false</disabled>
</definition>
<disabled>false</disabled>
</flow-definition>`
// create jenkins job
// create jenkins job
if err := client.CreateJob("pipeline", xml); err != nil {
log.Fatalln(err)
}
qitem, err := client.BuildJob("pipeline", jenkins.ReqParams{})
// Build Job and get BuildItem
qitem, err := client.BuildJob("pipeline", nil)
if err != nil {
log.Fatalln(err)
}
Expand All @@ -68,4 +87,7 @@ func main() {
return nil
})
}
```
```

# Documentation
See https://pkg.go.dev/github.com/joelee2012/go-jenkins
File renamed without changes.
1 change: 1 addition & 0 deletions jenkins/build_test.go → build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func setupBuild(t *testing.T) *BuildItem {
assert.Contains(t, strings.Join(output, ""), os.Getenv("JENKINS_VERSION"))
return build
}

func TestBuildItemIsBuilding(t *testing.T) {
build := setupBuild(t)
building, err := build.IsBuilding()
Expand Down
File renamed without changes.
File renamed without changes.
119 changes: 110 additions & 9 deletions jenkins/jenkins.go → jenkins.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import (
"github.com/imroc/req"
)

type JenkinsError struct {
}

type Client struct {
URL string
Header http.Header
Expand All @@ -31,12 +28,65 @@ type Crumb struct {
Value string `json:"crumb"`
}

// Create new Client
// client, err = NewClient(os.Getenv("JENKINS_URL"), os.Getenv("JENKINS_USER"), os.Getenv("JENKINS_PASSWORD"))
// if err != nil {
// return err
// }
// fmt.Println(client)
// Init Jenkins client and create job to build
// package main
//
// import (
// "log"
// "time"
//
// "github.com/joelee2012/go-jenkins/jenkins"
// )
//
// func main() {
// client, err := jenkins.NewClient("http://localhost:8080/", "admin", "1234")
// if err != nil {
// log.Fatalln(err)
// }
// xml := `<?xml version='1.1' encoding='UTF-8'?>
// <flow-definition plugin="workflow-job">
// <definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps">
// <script>#!groovy
// pipeline {
// agent any
// stages {
// stage('build'){
// steps{
// sh 'echo $JENKINS_VERSION'
// }
// }
// }
// }</script>
// <sandbox>true</sandbox>
// </definition>
// <disabled>false</disabled>
// </flow-definition>`
// // create jenkins job
// if err := client.CreateJob("pipeline", xml); err != nil {
// log.Fatalln(err)
// }
// qitem, err := client.BuildJob("pipeline", nil)
// if err != nil {
// log.Fatalln(err)
// }
// var build *Build
// for {
// time.Sleep(1 * time.Second)
// build, err = qitem.GetBuild()
// if err != nil {
// log.Fatalln(err)
// }
// if build != nil {
// break
// }
// }
// // tail the build log to end
// build.LoopProgressiveLog("text", func(line string) error {
// log.Println(line)
// time.Sleep(1 * time.Second)
// return nil
// })
// }
func NewClient(url, user, password string) (*Client, error) {
url = appendSlash(url)
c := &Client{URL: url, Header: make(http.Header), Req: req.New()}
Expand All @@ -53,6 +103,7 @@ func NewClient(url, user, password string) (*Client, error) {
return c, nil
}

// Set content type for request, default is 'application/json'
func (c *Client) SetContentType(ctype string) {
if ctype == "" {
c.Header.Set("Accept", "application/json")
Expand Down Expand Up @@ -84,11 +135,41 @@ func (c *Client) GetCrumb() (*Crumb, error) {
return c.Crumb, nil
}

// Get job with fullname:
// job, err := client.GetJob("path/to/job")
// if err != nil {
// return err
// }
// fmt.Println(job)
func (c *Client) GetJob(fullName string) (*JobItem, error) {
folder, shortName := c.resolveJob(fullName)
return folder.Get(shortName)
}

// Create job with given xml config:
// xml := `<?xml version='1.1' encoding='UTF-8'?>
// <flow-definition plugin="workflow-job">
// <definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps">
// <script>#!groovy
// pipeline {
// agent any
// stages {
// stage('build'){
// steps{
// echo "test message"
// }
// }
// }
// }
// </script>
// <sandbox>true</sandbox>
// </definition>
// <disabled>false</disabled>
// </flow-definition>`
// // create jenkins job
// if err := client.CreateJob("path/to/name", xml); err != nil {
// log.Fatalln(err)
// }
func (c *Client) CreateJob(fullName, xml string) error {
folder, shortName := c.resolveJob(fullName)
return folder.Create(shortName, xml)
Expand All @@ -108,6 +189,8 @@ func (c *Client) resolveJob(fullName string) (*JobItem, string) {
return NewJobItem(url, "Folder", c), name
}

// Covert fullname to url, eg:
// path/to/name -> http://jenkins/job/path/job/to/job/name
func (c *Client) Name2URL(fullName string) string {
if fullName == "" {
return c.URL
Expand All @@ -116,6 +199,8 @@ func (c *Client) Name2URL(fullName string) string {
return appendSlash(c.URL + "job/" + path)
}

// Covert url to full name, eg:
// http://jenkins/job/path/job/to/job/name -> path/to/name
func (c *Client) URL2Name(url string) (string, error) {
if !strings.HasPrefix(url, c.URL) {
return "", fmt.Errorf("%s is not in %s", url, c.URL)
Expand All @@ -124,6 +209,7 @@ func (c *Client) URL2Name(url string) (string, error) {
return strings.Trim(strings.ReplaceAll(path, "/job/", "/"), "/"), nil
}

// Get jenkins version number
func (c *Client) GetVersion() (string, error) {
resp, err := c.Req.Get(c.URL)
if err != nil {
Expand All @@ -132,15 +218,25 @@ func (c *Client) GetVersion() (string, error) {
return resp.Response().Header.Get("X-Jenkins"), nil
}

// Trigger job to build:
// // without parameters
// client.BuildJob("your job", nil)
// client.BuildJob("your job", jenkins.ReqParams{})
// // with parameters
// client.BuildJob("your job", jenkins.ReqParams{"ARG1": "ARG1_VALUE"})
func (c *Client) BuildJob(fullName string, params ReqParams) (*OneQueueItem, error) {
return NewJobItem(c.Name2URL(fullName), "Job", c).Build(params)
}

// List job with depth
func (c *Client) ListJobs(depth int) ([]*JobItem, error) {
job := NewJobItem(c.URL, "Folder", c)
return job.List(depth)
}

// Send request to jenkins,
// // send request to get JSON data of jenkins
// client.Request("GET", "api/json")
func (c *Client) Request(method, entry string, v ...interface{}) (*req.Resp, error) {
return c.Do(method, c.URL+entry, v...)
}
Expand Down Expand Up @@ -203,6 +299,11 @@ func (c *Client) ExportJCasC(name string) error {
return resp.ToFile(name)
}

// Bind jenkins JSON data to interface,
// // bind json data to map
// data := make(map[string]string)
// client.BindAPIJson(jenkins.ReqParams{"tree":"description"}, &data)
// fmt.Println(data["description"])
func (c *Client) BindAPIJson(params ReqParams, v interface{}) error {
resp, err := c.Request("GET", "api/json", params)
if err != nil {
Expand Down
File renamed without changes.
44 changes: 40 additions & 4 deletions jenkins/job.go → job.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package jenkins
import (
"encoding/json"
"fmt"
"net/url"
"path"
"strings"

Expand Down Expand Up @@ -93,9 +94,10 @@ func (j *JobItem) IsBuildable() (bool, error) {
}

func (j *JobItem) setName() {
j.FullName, _ = j.client.URL2Name(j.URL)
urlPath, _ := j.client.URL2Name(j.URL)
j.FullName, _ = url.PathUnescape(urlPath)
_, j.Name = path.Split(j.FullName)
j.FullDisplayName = strings.ReplaceAll(j.FullName, "/", " » ")
j.FullDisplayName, _ = url.PathUnescape(strings.ReplaceAll(j.FullName, "/", " » "))
}

func (j *JobItem) GetDescription() (string, error) {
Expand Down Expand Up @@ -146,7 +148,7 @@ func (j *JobItem) GetBuild(number int) (*BuildItem, error) {

for _, build := range jobJson.Builds {
if number == build.Number {
return NewBuildItem(build.URL, parseClass(build.Class), j.client), nil
return NewBuildItem(build.URL, build.Class, j.client), nil
}
}
return nil, nil
Expand Down Expand Up @@ -259,7 +261,41 @@ func (j *JobItem) ListBuilds() ([]*BuildItem, error) {
}

for _, build := range jobJson.Builds {
builds = append(builds, NewBuildItem(build.URL, parseClass(build.Class), j.client))
builds = append(builds, NewBuildItem(build.URL, build.Class, j.client))
}
return builds, nil
}

func (j *JobItem) SetNextBuildNumber(number int) error {
_, err := j.Request("POST", "nextbuildnumber/submit", ReqParams{"nextBuildNumber": number})
return err
}

func (j *JobItem) GetParameters() ([]*ParameterDefinition, error) {
jobJson := &Job{}
if err := j.BindAPIJson(nil, jobJson); err != nil {
return nil, err
}
for _, p := range jobJson.Property {
if p.Class == "hudson.model.ParametersDefinitionProperty" {
return p.ParameterDefinitions, nil
}
}
return nil, nil
}

func (j *JobItem) SCMPolling() error {
_, err := j.Request("POST", "polling")
return err
}

func (j *JobItem) GetMultibranchPipelineScanLog() (string, error) {
if j.Class != "WorkflowMultiBranchProject" {
return "", fmt.Errorf("%s is not a WorkflowMultiBranchProject", j)
}
resp, err := j.Request("POST", "indexing/consoleText")
if err != nil {
return "", err
}
return resp.String(), nil
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit bd9d63c

Please sign in to comment.