-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #50 from cloudogu/feature/scmm_pr_handling
Feature/scmm pr handling
- Loading branch information
Showing
7 changed files
with
689 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# top-most EditorConfig file | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
# Unix-style newlines with a newline ending every file | ||
end_of_line = lf | ||
charset = utf-8 | ||
insert_final_newline = true | ||
indent_size = 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package com.cloudogu.ces.cesbuildlib | ||
|
||
/** | ||
* An HTTP client that calls curl on the shell. | ||
* | ||
* Returns a map of | ||
* * httpCode (String) | ||
* * headers (Map) | ||
* * body (String) | ||
*/ | ||
class HttpClient implements Serializable { | ||
private script | ||
private credentials | ||
private Sh sh | ||
|
||
HttpClient(script, credentials = '') { | ||
this.script = script | ||
this.credentials = credentials | ||
this.sh = new Sh(script) | ||
} | ||
|
||
Map get(String url, String contentType = '', def data = '') { | ||
return httpRequest('GET', url, contentType, data) | ||
} | ||
|
||
Map put(String url, String contentType = '', def data = '') { | ||
return httpRequest('PUT', url, contentType, data) | ||
} | ||
|
||
Map post(String url, String contentType = '', def data = '') { | ||
return httpRequest('POST', url, contentType, data) | ||
} | ||
|
||
protected String executeWithCredentials(Closure closure) { | ||
if (credentials) { | ||
script.withCredentials([script.usernamePassword(credentialsId: credentials, | ||
passwordVariable: 'CURL_PASSWORD', usernameVariable: 'CURL_USER')]) { | ||
closure.call(true) | ||
} | ||
} else { | ||
closure.call(false) | ||
} | ||
} | ||
|
||
protected String getCurlAuthParam() { | ||
"-u ${script.env.CURL_USER}:${script.env.CURL_PASSWORD}" | ||
} | ||
|
||
protected Map httpRequest(String httpMethod, String url, String contentType, def data) { | ||
String httpResponse | ||
def rawHeaders | ||
def body | ||
|
||
executeWithCredentials { | ||
|
||
String curlCommand = | ||
"curl -i -X ${httpMethod} " + | ||
(credentials ? "${getCurlAuthParam()} " : '') + | ||
(contentType ? "-H 'Content-Type: ${contentType}' " : '') + | ||
(data ? "-d '${data.toString()}' " : '') + | ||
"${url}" | ||
|
||
// Command must be run inside this closure, otherwise the credentials will not be masked (using '*') in the console | ||
httpResponse = sh.returnStdOut curlCommand | ||
} | ||
|
||
String[] responseLines = httpResponse.split("\n") | ||
|
||
// e.g. HTTP/2 301 | ||
String httpCode = responseLines[0].split(" ")[1] | ||
def separatingLine = responseLines.findIndexOf { it.trim().isEmpty() } | ||
|
||
if (separatingLine > 0) { | ||
rawHeaders = responseLines[1..(separatingLine -1)] | ||
body = responseLines[separatingLine+1..-1].join('\n') | ||
} else { | ||
// No body returned | ||
rawHeaders = responseLines[1..-1] | ||
body = '' | ||
} | ||
|
||
def headers = [:] | ||
for(String line: rawHeaders) { | ||
// e.g. cache-control: no-cache | ||
def splitLine = line.split(':', 2); | ||
headers[splitLine[0].trim()] = splitLine[1].trim() | ||
} | ||
return [ httpCode: httpCode, headers: headers, body: body] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package com.cloudogu.ces.cesbuildlib | ||
|
||
import groovy.json.JsonOutput | ||
|
||
class SCMManager implements Serializable { | ||
|
||
private script | ||
protected HttpClient http | ||
protected String baseUrl | ||
|
||
SCMManager(script, String baseUrl, String credentials) { | ||
this.script = script | ||
this.baseUrl = baseUrl | ||
this.http = new HttpClient(script, credentials) | ||
} | ||
|
||
String searchPullRequestIdByTitle(String repository, String title) { | ||
def pullRequest | ||
for (Map pr : getPullRequests(repository)) { | ||
if (pr.title == title) { | ||
pullRequest = pr | ||
} | ||
} | ||
|
||
if (pullRequest) { | ||
return pullRequest.id.toString() | ||
} else { | ||
return '' | ||
} | ||
} | ||
|
||
String createPullRequest(String repository, String source, String target, String title, String description) { | ||
def dataJson = JsonOutput.toJson([ | ||
title : title, | ||
description: description, | ||
source : source, | ||
target : target | ||
]) | ||
def httpResponse = http.post(pullRequestEndpoint(repository), 'application/vnd.scmm-pullRequest+json;v=2', dataJson) | ||
|
||
script.echo "Creating pull request yields httpCode: ${httpResponse.httpCode}" | ||
if (httpResponse.httpCode != "201") { | ||
script.echo 'WARNING: Http status code indicates, that pull request was not created' | ||
return '' | ||
} | ||
|
||
// example: "location: https://some/pr/42" - extract id | ||
return httpResponse.headers.location.split("/")[-1] | ||
} | ||
|
||
boolean updatePullRequest(String repository, String pullRequestId, String title, String description) { | ||
// In order to update the description put in also the title. Otherwise the title is overwritten with an empty string. | ||
def dataJson = JsonOutput.toJson([ | ||
title : title, | ||
description: description | ||
]) | ||
|
||
def httpResponse = http.put("${pullRequestEndpoint(repository)}/${pullRequestId}", 'application/vnd.scmm-pullRequest+json;v=2', dataJson) | ||
|
||
script.echo "Pull request update yields http_code: ${httpResponse.httpCode}" | ||
if (httpResponse.httpCode != "204") { | ||
script.echo 'WARNING: Http status code indicates, that the pull request was not updated' | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
String createOrUpdatePullRequest(String repository, String source, String target, String title, String description) { | ||
|
||
def pullRequestId = searchPullRequestIdByTitle(repository, title) | ||
|
||
if(pullRequestId.isEmpty()) { | ||
return createPullRequest(repository, source, target, title, description) | ||
} else { | ||
if(updatePullRequest(repository, pullRequestId, title, description)) { | ||
return pullRequestId | ||
} else { | ||
return '' | ||
} | ||
} | ||
} | ||
|
||
boolean addComment(String repository, String pullRequestId, String comment) { | ||
def dataJson = JsonOutput.toJson([ | ||
comment: comment | ||
]) | ||
def httpResponse = http.post("${pullRequestEndpoint(repository)}/${pullRequestId}/comments", 'application/json', dataJson) | ||
|
||
script.echo "Adding comment yields http_code: ${httpResponse.httpCode}" | ||
if (httpResponse.httpCode != "201") { | ||
script.echo 'WARNING: Http status code indicates, that the comment was not added' | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
protected String pullRequestEndpoint(String repository) { | ||
"${this.baseUrl}/api/v2/pull-requests/${repository}" | ||
} | ||
|
||
/** | ||
* @return SCM-Manager's representation of PRs. Basically a list of PR objects. | ||
* properties (as of SCM-Manager 2.12.0) | ||
* * id | ||
* * author | ||
* * id | ||
* * displayName | ||
* * source - the source branch | ||
* * target - the target branch | ||
* * title | ||
* * description (branch) | ||
* * creationDate: (e.g. "2020-10-09T15:08:11.459Z") | ||
* * lastModified" | ||
* * status, e.g. "OPEN" | ||
* * reviewer (list) | ||
* * tasks | ||
* * todo (number) | ||
* * done (number | ||
* * tasks sourceRevision | ||
* * targetRevision | ||
* * targetRevision | ||
* * markedAsReviewed (list) | ||
* * emergencyMerged | ||
* * ignoredMergeObstacles | ||
*/ | ||
protected List getPullRequests(String repository) { | ||
def httpResponse = http.get(pullRequestEndpoint(repository), 'application/vnd.scmm-pullRequestCollection+json;v=2') | ||
|
||
script.echo "Getting all pull requests yields httpCode: ${httpResponse.httpCode}" | ||
if (httpResponse.httpCode != "200") { | ||
script.echo 'WARNING: Http status code indicates, that the pull requests could not be retrieved' | ||
return [] | ||
} | ||
|
||
def prsAsJson = script.readJSON text: httpResponse.body | ||
return prsAsJson._embedded.pullRequests | ||
} | ||
|
||
} |
Oops, something went wrong.