├── LICENSE ├── README.rst ├── computer.go ├── jenkins.go ├── jenkins_test.go ├── job.go ├── listview.go └── queue.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) Copyright © 2014 Kohei YOSHIDA 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the 5 | “Software”), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | golang-jenkins 2 | ============== 3 | 4 | .. image:: https://badges.gitter.im/Join%20Chat.svg 5 | :alt: Join the chat at https://gitter.im/yosida95/golang-jenkins 6 | :target: https://gitter.im/yosida95/golang-jenkins?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge 7 | 8 | ----- 9 | About 10 | ----- 11 | This is a API client of Jenkins API written in Go. 12 | 13 | ----- 14 | Usage 15 | ----- 16 | ``import "github.com/yosida95/golang-jenkins"`` 17 | 18 | Configure authentication and create an instance of the client: 19 | 20 | .. code-block:: go 21 | 22 | auth := &gojenkins.Auth{ 23 | Username: "[jenkins user name]", 24 | ApiToken: "[jenkins API token]", 25 | } 26 | jenkins := gojenkins.NewJenkins(auth, "[jenkins instance base url]") 27 | 28 | Make calls against the desired resources: 29 | 30 | .. code-block:: go 31 | 32 | job, err := jenkins.GetJob("[job name]") 33 | 34 | ------- 35 | License 36 | ------- 37 | golang-jenkins is licensed under the MIT LICENSE. 38 | See `./LICENSE <./LICENSE>`_. 39 | -------------------------------------------------------------------------------- /computer.go: -------------------------------------------------------------------------------- 1 | package gojenkins 2 | 3 | type ComputerObject struct { 4 | BusyExecutors int `json:"busyExecutors"` 5 | Computers []Computer `json:"computer"` 6 | DisplayName string `json:"displayName"` 7 | TotalExecutors int `json:"totalExecutors"` 8 | } 9 | 10 | type Computer struct { 11 | Actions []struct{} `json:"actions"` 12 | Class string `json:"_class"` 13 | DisplayName string `json:"displayName"` 14 | Executors []struct{} `json:"executors"` 15 | Idle bool `json:"idle"` 16 | JnlpAgent bool `json:"jnlpAgent"` 17 | LaunchSupported bool `json:"launchSupported"` 18 | ManualLaunchAllowed bool `json:"manualLaunchAllowed"` 19 | MonitorData struct { 20 | SwapSpaceMonitor struct { 21 | AvailablePhysicalMemory int64 `json:"availablePhysicalMemory"` 22 | AvailableSwapSpace int64 `json:"availableSwapSpace"` 23 | TotalPhysicalMemory int64 `json:"totalPhysicalMemory"` 24 | TotalSwapSpace int64 `json:"totalSwapSpace"` 25 | } `json:"hudson.node_monitors.SwapSpaceMonitor"` 26 | TemporarySpaceMonitor struct { 27 | Timestamp int64 `json:"timestamp"` 28 | Path string `json:"path"` 29 | Size int64 `json:"size"` 30 | } `json:"hudson.node_monitors.TemporarySpaceMonitor"` 31 | DiskSpaceMonitor struct { 32 | Timestamp int64 `json:"timestamp"` 33 | Path string `json:"path"` 34 | Size int64 `json:"size"` 35 | } `json:"hudson.node_monitors.DiskSpaceMonitor"` 36 | ArchitectureMonitor string `json:"hudson.node_monitors.ArchitectureMonitor"` 37 | ResponseTimeMonitor struct { 38 | Timestamp int64 `json:"timestamp"` 39 | Average int64 `json:"average"` 40 | } `json:"hudson.node_monitors.ResponseTimeMonitor"` 41 | ClockMonitor struct { 42 | Diff int64 `json:"diff"` 43 | } `json:"hudson.node_monitors.ClockMonitor"` 44 | } `json:"monitorData"` 45 | NumExecutors int `json:"numExecutors"` 46 | Offline bool `json:"offline"` 47 | OfflineCauseReason string `json:"offlineCauseReason"` 48 | TemporarilyOffline bool `json:"temporarilyOffline"` 49 | } 50 | -------------------------------------------------------------------------------- /jenkins.go: -------------------------------------------------------------------------------- 1 | package gojenkins 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "encoding/xml" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "net/http" 12 | "net/url" 13 | ) 14 | 15 | type HTTPStatusError struct { 16 | URL string 17 | Code int 18 | Status string 19 | } 20 | 21 | func (e *HTTPStatusError) Error() string { 22 | return fmt.Sprintf("bad http status: %d: %s", e.Code, e.Status) 23 | } 24 | 25 | type Auth struct { 26 | Username string 27 | ApiToken string 28 | } 29 | 30 | type Jenkins struct { 31 | auth *Auth 32 | baseUrl string 33 | client *http.Client 34 | } 35 | 36 | func NewJenkins(auth *Auth, baseUrl string) *Jenkins { 37 | return &Jenkins{ 38 | auth: auth, 39 | baseUrl: baseUrl, 40 | client: http.DefaultClient, 41 | } 42 | } 43 | 44 | // SetHTTPClient with timeouts or insecure transport, etc. 45 | func (jenkins *Jenkins) SetHTTPClient(client *http.Client) { 46 | jenkins.client = client 47 | } 48 | 49 | func (jenkins *Jenkins) buildUrl(path string, params url.Values) (requestUrl string) { 50 | requestUrl = jenkins.baseUrl + path + "/api/json" 51 | if params != nil { 52 | queryString := params.Encode() 53 | if queryString != "" { 54 | requestUrl = requestUrl + "?" + queryString 55 | } 56 | } 57 | 58 | return 59 | } 60 | 61 | // checkCrumb - checks if `useCrumb` is enabled and if so, retrieves crumb field and value and updates request header 62 | func (jenkins *Jenkins) checkCrumb(req *http.Request) (*http.Request, error) { 63 | 64 | // api - store jenkins api useCrumbs response 65 | api := struct { 66 | UseCrumbs bool `json:"useCrumbs"` 67 | }{} 68 | 69 | err := jenkins.get("/api/json", url.Values{"tree": []string{"useCrumbs"}}, &api) 70 | if err != nil { 71 | return req, err 72 | } 73 | 74 | if !api.UseCrumbs { 75 | // CSRF Protection is not enabled 76 | return req, nil 77 | } 78 | 79 | // get crumb field and value 80 | crumb := struct { 81 | Crumb string `json:"crumb"` 82 | CrumbRequestField string `json:"crumbRequestField"` 83 | }{} 84 | 85 | err = jenkins.get("/crumbIssuer", nil, &crumb) 86 | if err != nil { 87 | return req, err 88 | } 89 | 90 | // update header 91 | req.Header.Set(crumb.CrumbRequestField, crumb.Crumb) 92 | 93 | return req, nil 94 | } 95 | 96 | func (jenkins *Jenkins) sendRequest(req *http.Request) (*http.Response, error) { 97 | if jenkins.auth != nil { 98 | req.SetBasicAuth(jenkins.auth.Username, jenkins.auth.ApiToken) 99 | } 100 | res, err := jenkins.client.Do(req) 101 | if err != nil { 102 | return nil, err 103 | } 104 | if res.StatusCode != http.StatusOK { 105 | return nil, &HTTPStatusError{ 106 | URL: req.URL.String(), 107 | Code: res.StatusCode, 108 | Status: res.Status, 109 | } 110 | } 111 | return res, nil 112 | } 113 | 114 | func (jenkins *Jenkins) parseXmlResponse(resp *http.Response, body interface{}) (err error) { 115 | defer resp.Body.Close() 116 | 117 | if body == nil { 118 | return 119 | } 120 | 121 | data, err := ioutil.ReadAll(resp.Body) 122 | if err != nil { 123 | return 124 | } 125 | 126 | return xml.Unmarshal(data, body) 127 | } 128 | 129 | func (jenkins *Jenkins) parseResponse(resp *http.Response, body interface{}) (err error) { 130 | defer resp.Body.Close() 131 | 132 | if body == nil { 133 | return 134 | } 135 | 136 | data, err := ioutil.ReadAll(resp.Body) 137 | if err != nil { 138 | return 139 | } 140 | 141 | return json.Unmarshal(data, body) 142 | } 143 | 144 | func (jenkins *Jenkins) get(path string, params url.Values, body interface{}) (err error) { 145 | requestUrl := jenkins.buildUrl(path, params) 146 | req, err := http.NewRequest("GET", requestUrl, nil) 147 | if err != nil { 148 | return 149 | } 150 | 151 | resp, err := jenkins.sendRequest(req) 152 | if err != nil { 153 | return 154 | } 155 | return jenkins.parseResponse(resp, body) 156 | } 157 | 158 | func (jenkins *Jenkins) getXml(path string, params url.Values, body interface{}) (err error) { 159 | requestUrl := jenkins.buildUrl(path, params) 160 | req, err := http.NewRequest("GET", requestUrl, nil) 161 | if err != nil { 162 | return 163 | } 164 | 165 | resp, err := jenkins.sendRequest(req) 166 | if err != nil { 167 | return 168 | } 169 | return jenkins.parseXmlResponse(resp, body) 170 | } 171 | 172 | func (jenkins *Jenkins) post(path string, params url.Values, body interface{}) (err error) { 173 | requestUrl := jenkins.buildUrl(path, params) 174 | req, err := http.NewRequest("POST", requestUrl, nil) 175 | if err != nil { 176 | return 177 | } 178 | 179 | if _, err := jenkins.checkCrumb(req); err != nil { 180 | return err 181 | } 182 | 183 | resp, err := jenkins.sendRequest(req) 184 | if err != nil { 185 | return 186 | } 187 | if !(200 <= resp.StatusCode && resp.StatusCode <= 299) { 188 | return errors.New(fmt.Sprintf("error: HTTP POST returned status code %d (expected 2xx)", resp.StatusCode)) 189 | } 190 | 191 | return jenkins.parseResponse(resp, body) 192 | } 193 | func (jenkins *Jenkins) postXml(path string, params url.Values, xmlBody io.Reader, body interface{}) (err error) { 194 | requestUrl := jenkins.baseUrl + path 195 | if params != nil { 196 | queryString := params.Encode() 197 | if queryString != "" { 198 | requestUrl = requestUrl + "?" + queryString 199 | } 200 | } 201 | 202 | req, err := http.NewRequest("POST", requestUrl, xmlBody) 203 | if err != nil { 204 | return 205 | } 206 | 207 | if _, err := jenkins.checkCrumb(req); err != nil { 208 | return err 209 | } 210 | 211 | req.Header.Add("Content-Type", "application/xml") 212 | resp, err := jenkins.sendRequest(req) 213 | if err != nil { 214 | return 215 | } 216 | if resp.StatusCode != 200 { 217 | return errors.New(fmt.Sprintf("error: HTTP POST returned status code returned: %d", resp.StatusCode)) 218 | } 219 | 220 | return jenkins.parseXmlResponse(resp, body) 221 | } 222 | 223 | // GetJobs returns all jobs you can read. 224 | func (jenkins *Jenkins) GetJobs() ([]Job, error) { 225 | var payload = struct { 226 | Jobs []Job `json:"jobs"` 227 | }{} 228 | err := jenkins.get("", nil, &payload) 229 | return payload.Jobs, err 230 | } 231 | 232 | // GetJob returns a job which has specified name. 233 | func (jenkins *Jenkins) GetJob(name string) (job Job, err error) { 234 | err = jenkins.get(fmt.Sprintf("/job/%s", name), nil, &job) 235 | return 236 | } 237 | 238 | //GetJobConfig returns a maven job, has the one used to create Maven job 239 | func (jenkins *Jenkins) GetJobConfig(name string) (job MavenJobItem, err error) { 240 | err = jenkins.getXml(fmt.Sprintf("/job/%s/config.xml", name), nil, &job) 241 | return 242 | } 243 | 244 | // GetBuild returns a number-th build result of specified job. 245 | func (jenkins *Jenkins) GetBuild(job Job, number int) (build Build, err error) { 246 | err = jenkins.get(fmt.Sprintf("/job/%s/%d", job.Name, number), nil, &build) 247 | return 248 | } 249 | 250 | // GetLastBuild returns the last build of specified job. 251 | func (jenkins *Jenkins) GetLastBuild(job Job) (build Build, err error) { 252 | err = jenkins.get(fmt.Sprintf("/job/%s/lastBuild", job.Name), nil, &build) 253 | return 254 | } 255 | 256 | // Create a new job 257 | func (jenkins *Jenkins) CreateJob(mavenJobItem MavenJobItem, jobName string) error { 258 | mavenJobItemXml, _ := xml.Marshal(mavenJobItem) 259 | reader := bytes.NewReader(mavenJobItemXml) 260 | params := url.Values{"name": []string{jobName}} 261 | 262 | return jenkins.postXml("/createItem", params, reader, nil) 263 | } 264 | 265 | // Delete a job 266 | func (jenkins *Jenkins) DeleteJob(job Job) error { 267 | return jenkins.post(fmt.Sprintf("/job/%s/doDelete", job.Name), nil, nil) 268 | } 269 | 270 | // Add job to view 271 | func (jenkins *Jenkins) AddJobToView(viewName string, job Job) error { 272 | params := url.Values{"name": []string{job.Name}} 273 | return jenkins.post(fmt.Sprintf("/view/%s/addJobToView", viewName), params, nil) 274 | } 275 | 276 | // Create a new view 277 | func (jenkins *Jenkins) CreateView(listView ListView) error { 278 | xmlListView, _ := xml.Marshal(listView) 279 | reader := bytes.NewReader(xmlListView) 280 | params := url.Values{"name": []string{listView.Name}} 281 | 282 | return jenkins.postXml("/createView", params, reader, nil) 283 | } 284 | 285 | // Create a new build for this job. 286 | // Params can be nil. 287 | func (jenkins *Jenkins) Build(job Job, params url.Values) error { 288 | if hasParams(job) { 289 | return jenkins.post(fmt.Sprintf("/job/%s/buildWithParameters", job.Name), params, nil) 290 | } else { 291 | return jenkins.post(fmt.Sprintf("/job/%s/build", job.Name), params, nil) 292 | } 293 | } 294 | 295 | // Get the console output from a build. 296 | func (jenkins *Jenkins) GetBuildConsoleOutput(build Build) ([]byte, error) { 297 | requestUrl := fmt.Sprintf("%s/consoleText", build.Url) 298 | req, err := http.NewRequest("GET", requestUrl, nil) 299 | if err != nil { 300 | return nil, err 301 | } 302 | 303 | res, err := jenkins.sendRequest(req) 304 | if err != nil { 305 | return nil, err 306 | } 307 | 308 | defer res.Body.Close() 309 | return ioutil.ReadAll(res.Body) 310 | } 311 | 312 | // GetQueue returns the current build queue from Jenkins 313 | func (jenkins *Jenkins) GetQueue() (queue Queue, err error) { 314 | err = jenkins.get(fmt.Sprintf("/queue"), nil, &queue) 315 | return 316 | } 317 | 318 | // GetArtifact return the content of a build artifact 319 | func (jenkins *Jenkins) GetArtifact(build Build, artifact Artifact) ([]byte, error) { 320 | requestUrl := fmt.Sprintf("%s/artifact/%s", build.Url, artifact.RelativePath) 321 | req, err := http.NewRequest("GET", requestUrl, nil) 322 | if err != nil { 323 | return nil, err 324 | } 325 | 326 | res, err := jenkins.sendRequest(req) 327 | if err != nil { 328 | return nil, err 329 | } 330 | 331 | defer res.Body.Close() 332 | return ioutil.ReadAll(res.Body) 333 | } 334 | 335 | // SetBuildDescription sets the description of a build 336 | func (jenkins *Jenkins) SetBuildDescription(build Build, description string) error { 337 | requestUrl := fmt.Sprintf("%ssubmitDescription?description=%s", build.Url, url.QueryEscape(description)) 338 | req, err := http.NewRequest("GET", requestUrl, nil) 339 | if err != nil { 340 | return err 341 | } 342 | 343 | res, err := jenkins.sendRequest(req) 344 | if err != nil { 345 | return err 346 | } 347 | defer res.Body.Close() 348 | 349 | if res.StatusCode != 200 { 350 | return fmt.Errorf("Unexpected response: expected '200' but received '%d'", res.StatusCode) 351 | } 352 | 353 | return nil 354 | } 355 | 356 | // GetComputerObject returns the main ComputerObject 357 | func (jenkins *Jenkins) GetComputerObject() (co ComputerObject, err error) { 358 | err = jenkins.get(fmt.Sprintf("/computer"), nil, &co) 359 | return 360 | } 361 | 362 | // GetComputers returns the list of all Computer objects 363 | func (jenkins *Jenkins) GetComputers() ([]Computer, error) { 364 | var payload = struct { 365 | Computers []Computer `json:"computer"` 366 | }{} 367 | err := jenkins.get("/computer", nil, &payload) 368 | return payload.Computers, err 369 | } 370 | 371 | // GetComputer returns a Computer object with a specified name. 372 | func (jenkins *Jenkins) GetComputer(name string) (computer Computer, err error) { 373 | err = jenkins.get(fmt.Sprintf("/computer/%s", name), nil, &computer) 374 | return 375 | } 376 | 377 | // hasParams returns a boolean value indicating if the job is parameterized 378 | func hasParams(job Job) bool { 379 | for _, action := range job.Actions { 380 | if len(action.ParameterDefinitions) > 0 { 381 | return true 382 | } 383 | } 384 | return false 385 | } 386 | -------------------------------------------------------------------------------- /jenkins_test.go: -------------------------------------------------------------------------------- 1 | package gojenkins 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func NewJenkinsWithTestData() *Jenkins { 10 | var auth Auth 11 | return NewJenkins(&auth, "http://example.com") 12 | } 13 | 14 | func Test(t *testing.T) { 15 | jenkins := NewJenkinsWithTestData() 16 | jobs, err := jenkins.GetJobs() 17 | 18 | if err != nil { 19 | t.Errorf("error %v\n", err) 20 | } 21 | 22 | if len(jobs) == 0 { 23 | t.Errorf("return no jobs\n") 24 | } 25 | } 26 | 27 | func TestAddJobToView(t *testing.T) { 28 | jenkins := NewJenkinsWithTestData() 29 | 30 | scm := Scm{ 31 | Class: "hudson.scm.SubversionSCM", 32 | } 33 | jobItem := MavenJobItem{ 34 | Plugin: "maven-plugin@2.7.1", 35 | Description: "test description", 36 | Scm: scm, 37 | Triggers: Triggers{}, 38 | RunPostStepsIfResult: RunPostStepsIfResult{}, 39 | Settings: JobSettings{Class: "jenkins.mvn.DefaultSettingsProvider"}, 40 | GlobalSettings: JobSettings{Class: "jenkins.mvn.DefaultSettingsProvider"}, 41 | } 42 | newJobName := fmt.Sprintf("test-with-view-%d", time.Now().UnixNano()) 43 | newViewName := fmt.Sprintf("test-view-%d", time.Now().UnixNano()) 44 | jenkins.CreateJob(jobItem, newJobName) 45 | jenkins.CreateView(NewListView(newViewName)) 46 | 47 | job := Job{Name: newJobName} 48 | err := jenkins.AddJobToView(newViewName, job) 49 | 50 | if err != nil { 51 | t.Errorf("error %v\n", err) 52 | } 53 | } 54 | 55 | func TestCreateView(t *testing.T) { 56 | jenkins := NewJenkinsWithTestData() 57 | 58 | newViewName := fmt.Sprintf("test-view-%d", time.Now().UnixNano()) 59 | err := jenkins.CreateView(NewListView(newViewName)) 60 | 61 | if err != nil { 62 | t.Errorf("error %v\n", err) 63 | } 64 | } 65 | 66 | func TestCreateJobItem(t *testing.T) { 67 | jenkins := NewJenkinsWithTestData() 68 | scm := Scm{ 69 | ScmContent: ScmSvn{ 70 | Locations: Locations{ 71 | []ScmSvnLocation{ 72 | ScmSvnLocation{IgnoreExternalsOption: "false", DepthOption: "infinity", Local: ".", Remote: "http://some-svn-url"}, 73 | }, 74 | }, 75 | IgnoreDirPropChanges: "false", 76 | FilterChangelog: "false", 77 | WorkspaceUpdater: WorkspaceUpdater{Class: "hudson.scm.subversion.UpdateUpdater"}, 78 | }, 79 | Class: "hudson.scm.SubversionSCM", 80 | Plugin: "subversion@1.54", 81 | } 82 | triggers := Triggers{[]Trigger{ScmTrigger{}}} 83 | postStep := RunPostStepsIfResult{Name: "FAILURE", Ordinal: "2", Color: "RED", CompleteBuild: "true"} 84 | settings := JobSettings{Class: "jenkins.mvn.DefaultSettingsProvider"} 85 | globalSettings := JobSettings{Class: "jenkins.mvn.DefaultSettingsProvider"} 86 | jobItem := MavenJobItem{ 87 | Plugin: "maven-plugin@2.7.1", 88 | Description: "test description", 89 | Scm: scm, 90 | Triggers: triggers, 91 | RunPostStepsIfResult: postStep, 92 | Settings: settings, 93 | GlobalSettings: globalSettings, 94 | } 95 | 96 | newJobName := fmt.Sprintf("test-%d", time.Now().UnixNano()) 97 | err := jenkins.CreateJob(jobItem, newJobName) 98 | 99 | if err != nil { 100 | t.Errorf("error %v\n", err) 101 | } 102 | 103 | jobs, _ := jenkins.GetJobs() 104 | foundNewJob := false 105 | for _, v := range jobs { 106 | if v.Name == newJobName { 107 | foundNewJob = true 108 | } 109 | } 110 | 111 | if !foundNewJob { 112 | t.Errorf("error %s not found\n", newJobName) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /job.go: -------------------------------------------------------------------------------- 1 | package gojenkins 2 | 3 | import "encoding/xml" 4 | 5 | type Artifact struct { 6 | DisplayPath string `json:"displayPath"` 7 | FileName string `json:"fileName"` 8 | RelativePath string `json:"relativePath"` 9 | } 10 | 11 | type ScmAuthor struct { 12 | FullName string `json:"fullName"` 13 | AbsoluteUrl string `json:"absoluteUrl"` 14 | } 15 | 16 | type ScmChangeSetPath struct { 17 | EditType string `json:"editType"` 18 | File string `json:"File"` 19 | } 20 | 21 | type ChangeSetItem struct { 22 | AffectedPaths []string `json:"affectedPaths"` 23 | CommitId string `json:"commitId"` 24 | Timestamp int `json:"timestamp"` 25 | Author ScmAuthor `json:"author"` 26 | Comment string `json:"comment"` 27 | Date string `json:"date"` 28 | Id string `json:"id"` 29 | Message string `json:"msg"` 30 | Paths []ScmChangeSetPath `json:"paths"` 31 | } 32 | 33 | type ScmChangeSet struct { 34 | Kind string `json:"kind"` 35 | Items []ChangeSetItem `json:"items"` 36 | } 37 | 38 | type Build struct { 39 | Id string `json:"id"` 40 | Number int `json:"number"` 41 | Url string `json:"url"` 42 | 43 | FullDisplayName string `json:"fullDisplayName"` 44 | Description string `json:"description"` 45 | 46 | Timestamp int `json:"timestamp"` 47 | Duration int `json:"duration"` 48 | EstimatedDuration int `json:"estimatedDuration"` 49 | 50 | Building bool `json:"building"` 51 | KeepLog bool `json:"keepLog"` 52 | Result string `json:"result"` 53 | 54 | Artifacts []Artifact `json:"artifacts"` 55 | Actions []Action `json:"actions"` 56 | 57 | ChangeSet ScmChangeSet `json:"changeSet"` 58 | } 59 | 60 | type UpstreamCause struct { 61 | ShortDescription string `json:"shortDescription"` 62 | UpstreamBuild int `json:"upstreamBuild"` 63 | UpstreamProject string `json:"upstreamProject"` 64 | UpstreamUrl string `json:"upstreamUrl"` 65 | } 66 | 67 | type Job struct { 68 | Actions []Action `json:"actions"` 69 | Name string `json:"name"` 70 | Url string `json:"url"` 71 | Color string `json:"color"` 72 | 73 | Buildable bool `json:"buildable"` 74 | Builds []Build `json:"builds"` 75 | DisplayName string `json:"displayName"` 76 | Description string `json:"description"` 77 | HealthReport []Health `json:"healthReport"` 78 | 79 | LastCompletedBuild Build `json:"lastCompletedBuild"` 80 | LastFailedBuild Build `json:"lastFailedBuild"` 81 | LastStableBuild Build `json:"lastStableBuild"` 82 | LastSuccessfulBuild Build `json:"lastSuccessfulBuild"` 83 | LastUnstableBuild Build `json:"lastUnstableBuild"` 84 | LastUnsuccessfulBuild Build `json:"lastUnsuccessfulBuild"` 85 | 86 | Property []Property `json:"property"` 87 | } 88 | 89 | type Health struct { 90 | Description string `json:"description"` 91 | } 92 | 93 | type Property struct { 94 | Parameters []JobParameter `json:"parameterDefinitions"` 95 | } 96 | 97 | type JobParameter struct { 98 | Default Parameter `json:"defaultParameterValue"` 99 | Name string `json:"name"` 100 | Description string `json:"description"` 101 | Type string `json:"type"` 102 | Choices []string `json:"choices"` 103 | } 104 | 105 | type MavenJobItem struct { 106 | XMLName struct{} `xml:"maven2-moduleset"` 107 | Plugin string `xml:"plugin,attr"` 108 | Actions string `xml:"actions"` 109 | Description string `xml:"description"` 110 | KeepDependencies string `xml:"keepDependencies"` 111 | Properties JobProperties `xml:"properties"` 112 | Scm Scm `xml:"scm"` 113 | CanRoam string `xml:"canRoam"` 114 | Disabled string `xml:"disabled"` 115 | BlockBuildWhenDownstreamBuilding string `xml:"blockBuildWhenDownstreamBuilding"` 116 | BlockBuildWhenUpstreamBuilding string `xml:"blockBuildWhenUpstreamBuilding"` 117 | Triggers Triggers `xml:"triggers"` 118 | ConcurrentBuild string `xml:"concurrentBuild"` 119 | Goals string `xml:"goals"` 120 | AggregatorStyleBuild string `xml:"aggregatorStyleBuild"` 121 | IncrementalBuild string `xml:"incrementalBuild"` 122 | IgnoreUpstremChanges string `xml:"ignoreUpstremChanges"` 123 | ArchivingDisabled string `xml:"archivingDisabled"` 124 | SiteArchivingDisabled string `xml:"siteArchivingDisabled"` 125 | FingerprintingDisabled string `xml:"fingerprintingDisabled"` 126 | ResolveDependencies string `xml:"resolveDependencies"` 127 | ProcessPlugins string `xml:"processPlugins"` 128 | MavenName string `xml:"mavenName"` 129 | MavenValidationLevel string `xml:"mavenValidationLevel"` 130 | DefaultGoals string `xml:"defaultGoals"` 131 | RunHeadless string `xml:"runHeadless"` 132 | DisableTriggerDownstreamProjects string `xml:"disableTriggerDownstreamProjects"` 133 | Settings JobSettings `xml:"settings"` 134 | GlobalSettings JobSettings `xml:"globalSettings"` 135 | RunPostStepsIfResult RunPostStepsIfResult `xml:"runPostStepsIfResult"` 136 | Postbuilders PostBuilders `xml:"postbuilders"` 137 | } 138 | 139 | type Scm struct { 140 | ScmContent 141 | Class string `xml:"class,attr"` 142 | Plugin string `xml:"plugin,attr"` 143 | } 144 | 145 | type ScmContent interface{} 146 | 147 | type ScmSvn struct { 148 | Locations Locations `xml:"locations"` 149 | ExcludedRegions string `xml:"excludedRegions"` 150 | IncludedRegions string `xml:"includedRegions"` 151 | ExcludedUsers string `xml:"excludedUsers"` 152 | ExcludedRevprop string `xml:"excludedRevprop"` 153 | ExcludedCommitMessages string `xml:"excludedCommitMessages"` 154 | WorkspaceUpdater WorkspaceUpdater `xml:"workspaceUpdater"` 155 | IgnoreDirPropChanges string `xml:"ignoreDirPropChanges"` 156 | FilterChangelog string `xml:"filterChangelog"` 157 | } 158 | 159 | type WorkspaceUpdater struct { 160 | Class string `xml:"class,attr"` 161 | } 162 | type Locations struct { 163 | Location []ScmSvnLocation `xml:"hudson.scm.SubversionSCM_-ModuleLocation"` 164 | } 165 | 166 | type ScmSvnLocation struct { 167 | Remote string `xml:"remote"` 168 | Local string `xml:"local"` 169 | DepthOption string `xml:"depthOption"` 170 | IgnoreExternalsOption string `xml:"ignoreExternalsOption"` 171 | } 172 | 173 | type PostBuilders struct { 174 | XMLName xml.Name `xml:"postbuilders"` 175 | PostBuilder []PostBuilder 176 | } 177 | 178 | type PostBuilder interface { 179 | } 180 | 181 | type ShellBuilder struct { 182 | XMLName xml.Name `xml:"hudson.tasks.Shell"` 183 | Command string `xml:"command"` 184 | } 185 | 186 | type JobSettings struct { 187 | Class string `xml:"class,attr"` 188 | JobSetting []JobSetting 189 | } 190 | 191 | type JobSetting struct { 192 | } 193 | type JobProperties struct { 194 | } 195 | type Triggers struct { 196 | Trigger []Trigger 197 | } 198 | type Trigger interface { 199 | } 200 | type ScmTrigger struct { 201 | XMLName struct{} `xml:"hudson.triggers.SCMTrigger"` 202 | Spec string `xml:"spec"` 203 | IgnorePostCommitHooks string `xml:"ignorePostCommitHooks"` 204 | } 205 | type RunPostStepsIfResult struct { 206 | Name string `xml:"name"` 207 | Ordinal string `xml:"ordinal"` 208 | Color string `xml:"color"` 209 | CompleteBuild string `xml:"completeBuild"` 210 | } 211 | 212 | type ScmGit struct { 213 | UserRemoteConfigs UserRemoteConfigs `xml:"userRemoteConfigs"` 214 | Branches Branches `xml:"branches"` 215 | DoGenerateSubmoduleConfigurations bool `xml:"doGenerateSubmoduleConfigurations"` 216 | GitBrowser GitBrowser `xml:"browser"` 217 | GitSubmoduleCfg GitSubmoduleCfg `xml:"submoduleCfg"` 218 | GitExtensions GitExtensions `xml:"extensions"` 219 | } 220 | 221 | type UserRemoteConfigs struct { 222 | UserRemoteConfig UserRemoteConfig `xml:"hudson.plugins.git.UserRemoteConfig"` 223 | } 224 | 225 | type UserRemoteConfig struct { 226 | Urls []string `xml:"url"` 227 | } 228 | 229 | type Branches struct { 230 | BranchesSpec []BranchesSpec `xml:"hudson.plugins.git.BranchSpec"` 231 | } 232 | 233 | type BranchesSpec struct { 234 | Name string `xml:"name"` 235 | } 236 | 237 | type GitBrowser struct { 238 | Class string `xml:"class,attr"` 239 | Url string `xml:"url"` 240 | ProjectName string `xml:"projectName"` 241 | } 242 | 243 | type GitSubmoduleCfg struct { 244 | Class string `xml:"class,attr"` 245 | } 246 | 247 | type GitExtensions struct { 248 | Class string `xml:"class,attr"` 249 | LocalBranch LocalBranch `xml:"hudson.plugins.git.extensions.impl.LocalBranch"` 250 | } 251 | 252 | type LocalBranch struct { 253 | LocalBranch string `xml:"localBranch"` 254 | } 255 | 256 | //UnmarshalXML implements xml.UnmarshalXML intrface 257 | //Decode between multiple types of Scm. for now only SVN is supported 258 | func (iscm *Scm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 259 | for _, v := range start.Attr { 260 | if v.Name.Local == "class" { 261 | iscm.Class = v.Value 262 | } else if v.Name.Local == "plugin" { 263 | iscm.Plugin = v.Value 264 | } 265 | } 266 | switch iscm.Class { 267 | case "hudson.scm.SubversionSCM": 268 | iscm.ScmContent = &ScmSvn{} 269 | err := d.DecodeElement(&iscm.ScmContent, &start) 270 | if err != nil { 271 | return err 272 | } 273 | case "hudson.plugins.git.GitSCM": 274 | iscm.ScmContent = &ScmGit{} 275 | err := d.DecodeElement(&iscm.ScmContent, &start) 276 | if err != nil { 277 | return err 278 | } 279 | } 280 | return nil 281 | } 282 | -------------------------------------------------------------------------------- /listview.go: -------------------------------------------------------------------------------- 1 | package gojenkins 2 | 3 | import "encoding/xml" 4 | 5 | type ListView struct { 6 | XMLName xml.Name `xml:"hudson.model.ListView"` 7 | Name string `xml:"name"` 8 | FilterExecutors bool `xml:"filterExecutors"` 9 | FilterQueue bool `xml:"filterQueue"` 10 | Columns Columns `xml:"columns"` 11 | } 12 | 13 | func NewListView(name string) ListView { 14 | columns := Columns{Column: []Column{StatusColumn{}, WeatherColumn{}, JobColumn{}, LastSuccessColumn{}, LastFailureColumn{}, LastDurationColumn{}, BuildButtonColumn{}}} 15 | return ListView{Name: name, FilterExecutors: false, FilterQueue: false, Columns: columns} 16 | } 17 | 18 | type Column interface { 19 | } 20 | 21 | type Columns struct { 22 | XMLName xml.Name `xml:"columns"` 23 | Column []Column 24 | } 25 | 26 | type StatusColumn struct { 27 | XMLName xml.Name `xml:"hudson.views.StatusColumn"` 28 | } 29 | type WeatherColumn struct { 30 | XMLName xml.Name `xml:"hudson.views.WeatherColumn"` 31 | } 32 | 33 | type JobColumn struct { 34 | XMLName xml.Name `xml:"hudson.views.JobColumn"` 35 | } 36 | type LastSuccessColumn struct { 37 | XMLName xml.Name `xml:"hudson.views.LastSuccessColumn"` 38 | } 39 | type LastFailureColumn struct { 40 | XMLName xml.Name `xml:"hudson.views.LastFailureColumn"` 41 | } 42 | type LastDurationColumn struct { 43 | XMLName xml.Name `xml:"hudson.views.LastDurationColumn"` 44 | } 45 | type BuildButtonColumn struct { 46 | XMLName xml.Name `xml:"hudson.views.BuildButtonColumn"` 47 | } 48 | -------------------------------------------------------------------------------- /queue.go: -------------------------------------------------------------------------------- 1 | package gojenkins 2 | 3 | type Queue struct { 4 | Items []Item `json:"items"` 5 | } 6 | 7 | type Item struct { 8 | Actions []Action `json:"actions"` 9 | Blocked bool `json:"blocked"` 10 | Buildable bool `json:"buildable"` 11 | Id int `json:"id"` 12 | InQueueSince int64 `json:"inQueueSince"` 13 | Params string `json:"params"` 14 | Stuck bool `json:"stuck"` 15 | Task Task `json:"task"` 16 | URL string `json:"url"` 17 | Why string `json:"why"` 18 | BuildableStartMilliseconds int64 `json:"buildableStartMilliseconds"` 19 | Pending bool `json:"pending"` 20 | } 21 | 22 | type Action struct { 23 | Causes []Cause `json:"causes"` 24 | Parameter []Parameter `json:"parameters"` 25 | ParameterDefinitions []ParameterDefinition `json:"parameterDefinitions"` 26 | } 27 | 28 | type Cause struct { 29 | ShortDescription string `json:"shortDescription"` 30 | UserId string `json:"userId"` 31 | UserName string `json:"userName"` 32 | UpstreamCause 33 | } 34 | 35 | type ParameterDefinition struct { 36 | Name string `json:"name"` 37 | } 38 | 39 | // Parameter for a build 40 | type Parameter struct { 41 | Name string `json:"name"` 42 | Value interface{} `json:"value"` 43 | } 44 | 45 | type Task struct { 46 | Name string `json:"name"` 47 | Url string `json:"url"` 48 | Color string `json:"color"` 49 | } 50 | --------------------------------------------------------------------------------