Skip to content

Commit 1bb057a

Browse files
authored
Add build button and hide rebuild when it can't work. (#213)
* Add build button and hide rebuild when it can't work. * Change layout, fixes * Checkstyle * Fix UI * EOF * dot
1 parent 037ae6e commit 1bb057a

7 files changed

Lines changed: 213 additions & 76 deletions

File tree

github-pullrequest-plugin/src/main/java/com/github/kostyasha/github/integration/branch/GitHubBranchRepository.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package com.github.kostyasha.github.integration.branch;
22

33
import com.cloudbees.jenkins.GitHubWebHook;
4+
import com.github.kostyasha.github.integration.branch.trigger.JobRunnerForBranchCause;
45
import com.github.kostyasha.github.integration.generic.GitHubRepository;
56
import hudson.Functions;
67
import hudson.model.Item;
78
import hudson.model.Result;
89
import hudson.model.Run;
10+
import hudson.model.queue.QueueTaskFuture;
911
import hudson.util.FormValidation;
1012
import hudson.util.RunList;
1113
import jenkins.model.Jenkins;
@@ -26,6 +28,7 @@
2628
import static com.github.kostyasha.github.integration.branch.utils.JobHelper.ghBranchCauseFromRun;
2729
import static com.github.kostyasha.github.integration.branch.utils.JobHelper.ghBranchTriggerFromJob;
2830
import static java.util.Objects.isNull;
31+
import static java.util.Objects.nonNull;
2932
import static org.jenkinsci.plugins.github.pullrequest.utils.JobHelper.rebuild;
3033

3134
/**
@@ -82,7 +85,7 @@ public String getUrlName() {
8285
/**
8386
* Searches for all builds performed in the runs of current job.
8487
*
85-
* @return map with keys - string branch names and values - lists of related builds.
88+
* @return map with key - string branch names; value - lists of related builds.
8689
*/
8790
public Map<String, List<Run<?, ?>>> getAllBranchBuilds() {
8891

@@ -109,6 +112,7 @@ public String getUrlName() {
109112
@Override
110113
@RequirePOST
111114
public FormValidation doClearRepo() throws IOException {
115+
LOG.debug("Got clear GitHub Branch repo request for {}", getJob().getFullName());
112116
FormValidation result;
113117
try {
114118
Jenkins instance = GitHubWebHook.getJenkinsInstance();
@@ -156,7 +160,7 @@ public FormValidation doRunTrigger() throws IOException {
156160

157161
@Override
158162
@RequirePOST
159-
public FormValidation doRebuildFailed() throws IOException {
163+
public FormValidation doRebuildAllFailed() throws IOException {
160164
FormValidation result;
161165
try {
162166
Jenkins instance = GitHubWebHook.getJenkinsInstance();
@@ -179,6 +183,44 @@ public FormValidation doRebuildFailed() throws IOException {
179183
return result;
180184
}
181185

186+
@RequirePOST
187+
public FormValidation doBuild(StaplerRequest req) throws IOException {
188+
FormValidation result;
189+
190+
try {
191+
Jenkins instance = GitHubWebHook.getJenkinsInstance();
192+
if (!instance.hasPermission(Item.BUILD)) {
193+
return FormValidation.error("Forbidden");
194+
}
195+
196+
final String param = "branchName";
197+
String branchName = null;
198+
if (req.hasParameter(param)) {
199+
branchName = req.getParameter(param);
200+
}
201+
if (isNull(branchName) || !getBranches().containsKey(branchName)) {
202+
return FormValidation.error("No branch to build");
203+
}
204+
205+
final GitHubBranch localBranch = getBranches().get(branchName);
206+
final GitHubBranchCause cause = new GitHubBranchCause(localBranch, this, "Manual run.", false);
207+
final JobRunnerForBranchCause runner = new JobRunnerForBranchCause(getJob(),
208+
ghBranchTriggerFromJob(getJob()));
209+
final QueueTaskFuture<?> queueTaskFuture = runner.startJob(cause);
210+
211+
if (nonNull(queueTaskFuture)) {
212+
result = FormValidation.ok("Build scheduled");
213+
} else {
214+
result = FormValidation.warning("Build not scheduled");
215+
}
216+
} catch (Exception e) {
217+
LOG.error("Can't start build", e.getMessage());
218+
result = FormValidation.error(e, "Can't start build: " + e.getMessage());
219+
}
220+
221+
return result;
222+
}
223+
182224
@Override
183225
@RequirePOST
184226
public FormValidation doRebuild(StaplerRequest req) throws IOException {

github-pullrequest-plugin/src/main/java/com/github/kostyasha/github/integration/generic/GitHubRepository.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,16 @@ public synchronized void save() throws IOException {
140140

141141
public abstract FormValidation doRunTrigger() throws IOException;
142142

143-
public abstract FormValidation doRebuildFailed() throws IOException;
143+
public abstract FormValidation doRebuildAllFailed() throws IOException;
144144

145+
/**
146+
* Build using local PR state.
147+
*/
148+
public abstract FormValidation doBuild(StaplerRequest req) throws IOException;
149+
150+
/**
151+
* Rebuild latest built build. Actions copied.
152+
*/
145153
public abstract FormValidation doRebuild(StaplerRequest req) throws IOException;
146154

147155
}

github-pullrequest-plugin/src/main/java/org/jenkinsci/plugins/github/pullrequest/GitHubPRRepository.java

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
import hudson.model.Job;
99
import hudson.model.Result;
1010
import hudson.model.Run;
11+
import hudson.model.queue.QueueTaskFuture;
1112
import hudson.util.FormValidation;
1213
import hudson.util.RunList;
1314
import jenkins.model.Jenkins;
15+
import org.jenkinsci.plugins.github.pullrequest.trigger.JobRunnerForCause;
1416
import org.jenkinsci.plugins.github.pullrequest.utils.JobHelper;
1517
import org.kohsuke.github.GHRepository;
1618
import org.kohsuke.stapler.StaplerRequest;
@@ -27,7 +29,9 @@
2729
import java.util.Map;
2830

2931
import static java.util.Objects.isNull;
32+
import static java.util.Objects.nonNull;
3033
import static org.jenkinsci.plugins.github.pullrequest.utils.JobHelper.ghPRCauseFromRun;
34+
import static org.jenkinsci.plugins.github.pullrequest.utils.JobHelper.ghPRTriggerFromJob;
3135
import static org.jenkinsci.plugins.github.pullrequest.utils.JobHelper.rebuild;
3236

3337
/**
@@ -41,7 +45,7 @@ public class GitHubPRRepository extends GitHubRepository<GitHubPRRepository> {
4145
* Store constantly changing information in job directory with .runtime.xml tail
4246
*/
4347
public static final String FILE = GitHubPRRepository.class.getName() + ".runtime.xml";
44-
private static final Logger LOGGER = LoggerFactory.getLogger(GitHubPRRepository.class);
48+
private static final Logger LOG = LoggerFactory.getLogger(GitHubPRRepository.class);
4549

4650
private Map<Integer, GitHubPRPullRequest> pulls = new HashMap<>();
4751

@@ -76,7 +80,7 @@ public Map<Integer, GitHubPRPullRequest> getPulls() {
7680

7781
Map<Integer, List<Run<?, ?>>> map = new HashMap<>();
7882
final RunList<?> runs = job.getBuilds();
79-
LOGGER.debug("Got builds for job {}", job.getFullName());
83+
LOG.debug("Got builds for job {}", job.getFullName());
8084

8185
for (Run<?, ?> run : runs) {
8286
GitHubPRCause cause = ghPRCauseFromRun(run);
@@ -122,7 +126,7 @@ public FormValidation doClearRepo() throws IOException {
122126
result = FormValidation.error("Forbidden");
123127
}
124128
} catch (Exception e) {
125-
LOGGER.error("Can\'t delete repository file '{}'",
129+
LOG.error("Can\'t delete repository file '{}'",
126130
configFile.getFile().getAbsolutePath(), e);
127131
result = FormValidation.error(e, "Can't delete: %s", e.getMessage());
128132
}
@@ -142,24 +146,24 @@ public FormValidation doRunTrigger() {
142146
if (trigger != null) {
143147
trigger.run();
144148
result = FormValidation.ok("GitHub PR trigger run");
145-
LOGGER.debug("GitHub PR trigger run for {}", job);
149+
LOG.debug("GitHub PR trigger run for {}", job);
146150
} else {
147-
LOGGER.error("GitHub PR trigger not available for {}", job);
151+
LOG.error("GitHub PR trigger not available for {}", job);
148152
result = FormValidation.error("GitHub PR trigger not available");
149153
}
150154
} else {
151-
LOGGER.warn("No permissions to run GitHub PR trigger");
155+
LOG.warn("No permissions to run GitHub PR trigger");
152156
result = FormValidation.error("Forbidden");
153157
}
154158
} catch (Exception e) {
155-
LOGGER.error("Can't run trigger", e);
159+
LOG.error("Can't run trigger", e);
156160
result = FormValidation.error(e, "Can't run trigger: %s", e.getMessage());
157161
}
158162
return result;
159163
}
160164

161165
@RequirePOST
162-
public FormValidation doRebuildFailed() throws IOException {
166+
public FormValidation doRebuildAllFailed() throws IOException {
163167
FormValidation result;
164168
try {
165169
Jenkins instance = GitHubWebHook.getJenkinsInstance();
@@ -176,12 +180,51 @@ public FormValidation doRebuildFailed() throws IOException {
176180
result = FormValidation.error("Forbidden");
177181
}
178182
} catch (Exception e) {
179-
LOGGER.error("Can't start rebuild", e);
183+
LOG.error("Can't start rebuild", e);
180184
result = FormValidation.error(e, "Can't start rebuild: %s", e.getMessage());
181185
}
182186
return result;
183187
}
184188

189+
@Override
190+
public FormValidation doBuild(StaplerRequest req) throws IOException {
191+
FormValidation result;
192+
193+
try {
194+
Jenkins instance = GitHubWebHook.getJenkinsInstance();
195+
if (!instance.hasPermission(Item.BUILD)) {
196+
return FormValidation.error("Forbidden");
197+
}
198+
199+
final String prNumberParam = "prNumber";
200+
int prId = 0;
201+
if (req.hasParameter(prNumberParam)) {
202+
prId = Integer.valueOf(req.getParameter(prNumberParam));
203+
}
204+
205+
if (prId == 0 || !getPulls().containsKey(prId)) {
206+
return FormValidation.error("No branch to build");
207+
}
208+
209+
final GitHubPRPullRequest localPR = getPulls().get(prId);
210+
final GitHubPRCause cause = new GitHubPRCause(localPR, null, false, "Manual run.");
211+
212+
final JobRunnerForCause runner = new JobRunnerForCause(getJob(), ghPRTriggerFromJob(getJob()));
213+
final QueueTaskFuture<?> queueTaskFuture = runner.startJob(cause);
214+
215+
if (nonNull(queueTaskFuture)) {
216+
result = FormValidation.ok("Build scheduled");
217+
} else {
218+
result = FormValidation.warning("Build not scheduled");
219+
}
220+
} catch (Exception e) {
221+
LOG.error("Can't start build", e.getMessage());
222+
result = FormValidation.error(e, "Can't start build: " + e.getMessage());
223+
}
224+
225+
return result;
226+
}
227+
185228
@RequirePOST
186229
public FormValidation doRebuild(StaplerRequest req) throws IOException {
187230
FormValidation result;
@@ -210,7 +253,7 @@ public FormValidation doRebuild(StaplerRequest req) throws IOException {
210253
result = FormValidation.warning("Build not found");
211254
}
212255
} catch (Exception e) {
213-
LOGGER.error("Can't start rebuild", e);
256+
LOG.error("Can't start rebuild", e);
214257
result = FormValidation.error(e, "Can't start rebuild: " + e.getMessage());
215258
}
216259
return result;

github-pullrequest-plugin/src/main/resources/com/github/kostyasha/github/integration/branch/GitHubBranchRepository/index.groovy

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,47 @@ package com.github.kostyasha.github.integration.branch.GitHubBranchRepository
22

33
import com.github.kostyasha.github.integration.branch.GitHubBranchCause
44
import hudson.model.Item
5-
import org.apache.commons.lang3.StringEscapeUtils
5+
import lib.FormTagLib
6+
import lib.LayoutTagLib
67

7-
def f = namespace(lib.FormTagLib);
8-
def l = namespace(lib.LayoutTagLib);
9-
def t = namespace("/lib/hudson")
8+
import static org.apache.commons.lang3.StringEscapeUtils.escapeEcmaScript
9+
10+
def f = namespace(FormTagLib);
11+
def l = namespace(LayoutTagLib);
1012
def st = namespace("jelly:stapler");
11-
def makeBuildItem(def runs) {
12-
a("Related builds: ")
13-
for (build in runs) {
14-
a(href: rootURL + "/" + build.url + "console/") {
15-
img(src: rootURL + "/images/16x16/" + build.buildStatusUrl)
13+
14+
def printRelatedBuilds(def runs) {
15+
if (runs != null && !runs.isEmpty()) {
16+
a("Related builds: ")
17+
for (build in runs) {
18+
a(href: rootURL + "/" + build.url + "console/") {
19+
img(src: rootURL + "/images/16x16/" + build.buildStatusUrl)
20+
}
21+
a(href: rootURL + "/" + build.url, build.displayName, title: build.getCause(GitHubBranchCause.class).reason)
22+
text(" ")
1623
}
17-
a(href: rootURL + "/" + build.url, build.displayName, title:build.getCause(GitHubBranchCause.class).reason)
18-
text(" ")
24+
} else {
25+
a("No related builds.")
1926
}
2027
}
2128

22-
def escapeBranchName(def branchName) {
23-
// escape anything that isn't alphanumeric
24-
return StringEscapeUtils.escapeEcmaScript(branchName);
29+
static def makeRebuildResultId(def branchName) {
30+
// replace anything that isn't alphanumeric so it's valid html
31+
return ("rebuildResult" + branchName).replaceAll(/([^a-zA-Z0-9])/, '');
2532
}
2633

27-
def makeRebuildId(def branchName) {
34+
static def makeBuildResultId(def branchName) {
2835
// replace anything that isn't alphanumeric so it's valid html
29-
return ("rebuildResult" + branchName).replaceAll(/([^a-zA-Z0-9])/, '');
36+
return ("buildResult" + branchName).replaceAll(/([^a-zA-Z0-9])/, '');
3037
}
3138

3239
l.layout(title: "GitHub Branch Status") {
3340
st.include(page: "sidepanel", it: my.job)
34-
script(src:"${rootURL}${h.getResourcePath()}/plugin/github-pullrequest/scripts/featureButton.js")
41+
script(src: "${rootURL}${h.getResourcePath()}/plugin/github-pullrequest/scripts/featureButton.js")
3542
l.main_panel() {
3643
h1("GitHub Branch Status");
3744
text("Repository: ")
38-
a(href:my.githubUrl, my.fullName)
39-
45+
a(href: my.githubUrl, my.fullName)
4046
br()
4147
br()
4248
div(style: "display: inline-block") {
@@ -53,39 +59,59 @@ l.layout(title: "GitHub Branch Status") {
5359
def buildMap = my.getAllBranchBuilds()
5460
table() {
5561
for (branch in my.branches.values()) {
62+
def branchBuilds = buildMap.get(branch.name);
5663
tr() {
5764
td() {
5865
br()
66+
// info about branch itself
5967
st.include(page: "index", it: branch)
6068
}
6169
}
6270
tr() {
63-
td() { makeBuildItem(buildMap.get(branch.name)) }
71+
td() { printRelatedBuilds(branchBuilds) }
6472
}
73+
// build local Branch button
6574
if (h.hasPermission(Item.BUILD)) {
6675
tr() {
6776
td() {
68-
def rebuildId = makeRebuildId(branch.name);
69-
def escaped = escapeBranchName(branch.name);
70-
form(method: "post", action: "rebuild",
71-
onsubmit: "return callFeature(this, ${rebuildId}, {'branchName' : '${escaped}' })") {
72-
f.submit(value: _("Rebuild"))
73-
div(id: rebuildId)
77+
def buildResultId = makeBuildResultId(branch.name);
78+
// escape anything that isn't alphanumeric
79+
def escapedBuild = escapeEcmaScript(branch.name);
80+
form(method: "post", action: "build",
81+
onsubmit: "return callFeature(this, ${buildResultId}, {'branchName' : '${escapedBuild}' })",
82+
style: "float:left") {
83+
f.submit(value: _("Build"))
84+
div(id: buildResultId) // some text from responce
85+
}
86+
87+
// rebuild button
88+
if (branchBuilds != null && !branchBuilds.isEmpty()) {
89+
def rebuildResultId = makeRebuildResultId(branch.name);
90+
// escape anything that isn't alphanumeric
91+
def escapedRebuild = escapeEcmaScript(branch.name);
92+
form(method: "post",
93+
action: "rebuild",
94+
onsubmit: "return callFeature(this, ${rebuildResultId}, {'branchName' : '${escapedRebuild}' })",
95+
style: "float: left; margin-right: 100px") {
96+
f.submit(value: _("Rebuild last branch build"))
97+
div(id: rebuildResultId) // some text from responce
98+
}
7499
}
75100
}
76101
}
77102
}
78103
}
79104
}
80105
br()
81-
82106
div(style: "display: inline-block") {
83107
if (h.hasPermission(Item.BUILD)) {
84-
def rebuildFailedId = "rebuildFailedResult";
85-
form(method: "post", action: "rebuildFailed", onsubmit: "return callFeature(this, ${rebuildFailedId})",
108+
def rebuildAllFailedId = "rebuildFailedResult";
109+
form(method: "post",
110+
action: "rebuildAllFailed",
111+
onsubmit: "return callFeature(this, ${rebuildAllFailedId})",
86112
style: "float: right; margin-right: 100px") {
87113
f.submit(value: _("Rebuild all failed builds"))
88-
div(id: rebuildFailedId)
114+
div(id: rebuildAllFailedId)
89115
}
90116
}
91117

0 commit comments

Comments
 (0)