Skip to content

Commit 3b70475

Browse files
committed
http/2 only returns status code
1 parent 27b9641 commit 3b70475

File tree

3 files changed

+201
-2
lines changed

3 files changed

+201
-2
lines changed

bin/main/GithubPRPlanPlugin.groovy

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import static TerraformEnvironmentStage.PLAN
2+
import groovy.json.JsonOutput
3+
import groovy.json.JsonSlurper
4+
5+
class GithubPRPlanPlugin implements TerraformPlanCommandPlugin, TerraformEnvironmentStagePlugin, Resettable {
6+
7+
private static String myRepoSlug
8+
private static String myRepoHost
9+
private static String githubTokenEnvVar = "GITHUB_TOKEN"
10+
private static final int MAX_COMMENT_LENGTH = 65535
11+
12+
public static void init() {
13+
GithubPRPlanPlugin plugin = new GithubPRPlanPlugin()
14+
15+
TerraformEnvironmentStage.addPlugin(plugin)
16+
TerraformPlanCommand.addPlugin(plugin)
17+
}
18+
19+
public static withRepoSlug(String newRepoSlug) {
20+
GithubPRPlanPlugin.myRepoSlug = newRepoSlug
21+
return this
22+
}
23+
24+
public static withRepoHost(String newRepoHost) {
25+
myRepoHost = newRepoHost
26+
return this
27+
}
28+
29+
public static withGithubTokenEnvVar(String githubTokenEnvVar) {
30+
GithubPRPlanPlugin.githubTokenEnvVar = githubTokenEnvVar
31+
return this
32+
}
33+
34+
@Override
35+
public void apply(TerraformEnvironmentStage stage) {
36+
stage.decorate(PLAN, addComment(stage.getEnvironment()))
37+
}
38+
39+
@Override
40+
public void apply(TerraformPlanCommand command) {
41+
command.withPrefix("set -o pipefail;")
42+
command.withStandardErrorRedirection('plan.err')
43+
command.withSuffix('| tee plan.out')
44+
}
45+
46+
public Closure addComment(String env) {
47+
return { closure ->
48+
try {
49+
closure()
50+
} catch (err) {
51+
echo "terraform plan failed:"
52+
sh "cat plan.err"
53+
throw err
54+
}
55+
56+
if (isPullRequest()) {
57+
String url = getPullRequestCommentUrl()
58+
String comment = getCommentBody(env)
59+
postPullRequestComment(url, comment)
60+
}
61+
}
62+
}
63+
64+
public String getRepoSlug() {
65+
if (myRepoSlug != null) {
66+
return myRepoSlug
67+
}
68+
69+
def parsedScmUrl = Jenkinsfile.instance.getParsedScmUrl()
70+
def organization = parsedScmUrl['organization']
71+
def repo = parsedScmUrl['repo']
72+
73+
return "${organization}/${repo}"
74+
}
75+
76+
public String getRepoHost() {
77+
if (myRepoHost != null) {
78+
return myRepoHost
79+
}
80+
81+
def parsedScmUrl = Jenkinsfile.instance.getParsedScmUrl()
82+
def protocol = parsedScmUrl['protocol']
83+
def domain = parsedScmUrl['domain']
84+
85+
// We cannot post using the git protocol, change to https
86+
if (protocol == "git") {
87+
protocol = "https"
88+
}
89+
90+
return "${protocol}://${domain}"
91+
}
92+
93+
public String getBranchName() {
94+
return Jenkinsfile.instance.getEnv().BRANCH_NAME
95+
}
96+
97+
public boolean isPullRequest() {
98+
def branchName = getBranchName()
99+
100+
return branchName.startsWith('PR-')
101+
}
102+
103+
public String getPullRequestNumber() {
104+
def branchName = getBranchName()
105+
106+
return branchName.replace('PR-', '')
107+
}
108+
109+
public String getPullRequestCommentUrl() {
110+
def repoHost = getRepoHost()
111+
def repoSlug = getRepoSlug()
112+
def pullRequestNumber = getPullRequestNumber()
113+
114+
return "${repoHost}/api/v3/repos/${repoSlug}/issues/${pullRequestNumber}/comments".toString()
115+
}
116+
117+
public String readFile(String filename) {
118+
def original = Jenkinsfile.instance.original
119+
if (original.fileExists(filename)) {
120+
return original.readFile(filename)
121+
}
122+
123+
return null
124+
}
125+
126+
public String getPlanOutput() {
127+
def planOutput = readFile('plan.out')
128+
def planError = readFile('plan.err')
129+
// Skip any file outputs when the file does not exist
130+
def outputs = [planOutput, planError] - null
131+
132+
// Strip any ANSI color encodings and whitespaces
133+
def results = outputs.collect { output ->
134+
output.replaceAll(/\u001b\[[0-9;]+m/, '')
135+
.replace(/^\[[0-9;]+m/, '')
136+
.trim()
137+
}
138+
139+
// Separate by STDERR header if plan.err is not empty
140+
results.findAll { it != '' }
141+
.join('\nSTDERR:\n')
142+
}
143+
144+
public String getBuildResult() {
145+
Jenkinsfile.instance.original.currentBuild.currentResult
146+
}
147+
148+
public String getBuildUrl() {
149+
Jenkinsfile.instance.original.build_url
150+
}
151+
152+
public String getCommentBody(String environment) {
153+
def planOutput = getPlanOutput()
154+
def buildResult = getBuildResult()
155+
def buildUrl = getBuildUrl()
156+
def lines = []
157+
lines << "**Jenkins plan results for ${environment}** - ${buildResult} ( ${buildUrl} ):"
158+
lines << ''
159+
lines << '```'
160+
lines << planOutput
161+
lines << '```'
162+
lines << ''
163+
164+
return lines.join('\n')
165+
}
166+
167+
public postPullRequestComment(String pullRequestUrl, String commentBody) {
168+
def closure = { ->
169+
echo "Creating comment in GitHub"
170+
// GitHub can't handle comments of 65536 or longer; chunk
171+
commentBody.split("(?<=\\G.{${MAX_COMMENT_LENGTH}})").each { chunk ->
172+
def data = JsonOutput.toJson([body: chunk])
173+
def tmpDir = pwd(tmp: true)
174+
def bodyPath = "${tmpDir}/body.txt"
175+
writeFile(file: bodyPath, text: data)
176+
177+
def cmd = "curl -H \"Authorization: token \$${githubTokenEnvVar}\" -X POST -d @${bodyPath} -H 'Content-Type: application/json' -D comment.headers ${pullRequestUrl}"
178+
179+
def output = sh(script: cmd, returnStdout: true).trim()
180+
181+
def headers = readFile('comment.headers').trim()
182+
if (! (headers.contains('HTTP/1.1 201 Created') || headers.contains('HTTP/2 201'))) {
183+
error("Creating GitHub comment failed: ${headers}\n")
184+
}
185+
// ok, success
186+
def decoded = new JsonSlurper().parseText(output)
187+
echo "Created comment ${decoded.id} - ${decoded.html_url}"
188+
}
189+
}
190+
191+
closure.delegate = Jenkinsfile.original
192+
closure()
193+
}
194+
195+
public static void reset() {
196+
myRepoSlug = null
197+
myRepoHost = null
198+
}
199+
}

src/GithubPRPlanPlugin.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class GithubPRPlanPlugin implements TerraformPlanCommandPlugin, TerraformEnviron
179179
def output = sh(script: cmd, returnStdout: true).trim()
180180

181181
def headers = readFile('comment.headers').trim()
182-
if (! (headers.contains('HTTP/1.1 201 Created') || headers.contains('HTTP/2 201 Created'))) {
182+
if (! (headers.contains('HTTP/1.1 201 Created') || headers.contains('HTTP/2 201'))) {
183183
error("Creating GitHub comment failed: ${headers}\n")
184184
}
185185
// ok, success

test/GithubPRPlanPluginTest.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ class GithubPRPlanPluginTest {
454454
MockJenkinsfile.withParsedScmUrl([protocol: 'git', domain: 'my.github.com'])
455455
Jenkinsfile.original = spy(new MockWorkflowScript())
456456
def plugin = spy(new GithubPRPlanPlugin())
457-
doReturn('HTTP/2 201 Created').when(plugin).readFile('comment.headers')
457+
doReturn('HTTP/2 201').when(plugin).readFile('comment.headers')
458458
doReturn('{ "id": "someId", "html_url": "some_url" }').when(Jenkinsfile.original).sh(anyObject())
459459

460460
plugin.postPullRequestComment('someUrl', 'myPrComment')

0 commit comments

Comments
 (0)