8000 Merge pull request #119 from lanwen/st_setter · github-cloud/github-plugin@350802c · GitHub
[go: up one dir, main page]

Skip to content

Commit 350802c

Browse files
committed
Merge pull request jenkinsci#119 from lanwen/st_setter
Extended setter for status
2 parents 28d8dec + 66917ef commit 350802c

File tree

56 files changed

+1970
-135
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1970
-135
lines changed

src/main/java/com/cloudbees/jenkins/GitHubCommitNotifier.java

Lines changed: 45 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.cloudbees.jenkins;
22

3+
import edu.umd.cs.findbugs.annotations.NonNull;
34
import hudson.Extension;
45
import hudson.FilePath;
56
import hudson.Launcher;
6-
import hudson.Util;
77
import hudson.model.AbstractProject;
88
import hudson.model.Result;
99
import hudson.model.Run;
@@ -14,32 +14,38 @@
1414
import hudson.tasks.Publisher;
1515
import hudson.util.ListBoxModel;
1616
import jenkins.tasks.SimpleBuildStep;
17-
import org.eclipse.jgit.lib.ObjectId;
1817
import org.jenkinsci.plugins.github.common.ExpandableMessage;
19-
import org.jenkinsci.plugins.github.util.BuildDataHelper;
18+
import org.jenkinsci.plugins.github.extension.status.StatusErrorHandler;
19+
import org.jenkinsci.plugins.github.status.GitHubCommitStatusSetter;
20+
import org.jenkinsci.plugins.github.status.err.ChangingBuildStatusErrorHandler;
21+
import org.jenkinsci.plugins.github.status.err.ShallowAnyErrorHandler;
22+
import org.jenkinsci.plugins.github.status.sources.AnyDefinedRepositorySource;
23+
import org.jenkinsci.plugins.github.status.sources.BuildDataRevisionShaSource;
24+
import org.jenkinsci.plugins.github.status.sources.ConditionalStatusResultSource;
25+
import org.jenkinsci.plugins.github.status.sources.DefaultCommitContextSource;
26+
import org.jenkinsci.plugins.github.status.sources.DefaultStatusResultSource;
2027
import org.kohsuke.accmod.Restricted;
2128
import org.kohsuke.accmod.restrictions.NoExternalUse;
2229
import org.kohsuke.github.GHCommitState;
23-
import org.kohsuke.github.GHRepository;
2430
import org.kohsuke.stapler.DataBoundConstructor;
2531
import org.kohsuke.stapler.DataBoundSetter;
2632
import org.slf4j.Logger;
2733
import org.slf4j.LoggerFactory;
2834

2935
import javax.annotation.Nonnull;
30-
import java.io.FileNotFoundException;
3136
import java.io.IOException;
37+
import java.util.Collections;
3238

3339
import static com.cloudbees.jenkins.Messages.GitHubCommitNotifier_DisplayName;
34-
import static com.cloudbees.jenkins.Messages.GitHubCommitNotifier_SettingCommitStatus;
35-
import static com.coravy.hudson.plugins.github.GithubProjectProperty.displayNameFor;
3640
import static com.google.common.base.Objects.firstNonNull;
3741
import static hudson.model.Result.FAILURE;
3842
import static hudson.model.Result.SUCCESS;
3943
import static hudson.model.Result.UNSTABLE;
40-
import static java.lang.String.format;
41-
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
44+
import static java.util.Arrays.asList;
45+
import static org.apache.commons.lang3.StringUtils.isNotBlank;
4246
import static org.apache.commons.lang3.StringUtils.trimToEmpty;
47+
import static org.jenkinsci.plugins.github.status.sources.misc.AnyBuildResult.onAnyResult;
48+
import static org.jenkinsci.plugins.github.status.sources.misc.BetterThanOrEqualBuildResult.betterThanOrEqualTo;
4349

4450
/**
4551
* Create commit status notifications on the commits based on the outcome of the build.
@@ -107,105 +113,41 @@ public BuildStepMonitor getRequiredMonitorService() {
107113
}
108114

109115
@Override
110-
public void perform(Run<?, ?> build,
111-
FilePath ws,
112-
Launcher launcher,
113-
TaskListener listener) throws InterruptedException, IOException {
114-
try {
115-
updateCommitStatus(build, listener);
116-
} catch (IOException error) {
117-
final Result buildResult = getEffectiveResultOnFailure();
118-
if (buildResult.equals(FAILURE)) {
119-
throw error;
120-
} else {
121-
listener.error(format("[GitHub Commit Notifier] - %s", error.getMessage()));
122-
listener.getLogger().println(
123-
format("[GitHub Commit Notifier] - Build result will be set to %s", buildResult)
124-
);
125-
build.setResult(buildResult);
126-
}
127-
}
128-
}
129-
130-
private void updateCommitStatus(@Nonnull Run<?, ?> build,
131-
@Nonnull TaskListener listener) throws InterruptedException, IOException {
132-
final String sha1 = ObjectId.toString(BuildDataHelper.getCommitSHA1(build));
133-
134-
StatusResult status = statusFrom(build);
135-
String message = defaultIfEmpty(firstNonNull(statusMessage, DEFAULT_MESSAGE)
136-
.expandAll(build, listener), status.getMsg());
137-
String contextName = displayNameFor(build.getParent());
138-
139-
for (GitHubRepositoryName name : GitHubRepositoryNameContributor.parseAssociatedNames(build.getParent())) {
140-
for (GHRepository repository : name.resolve()) {
141-
142-
listener.getLogger().println(
143-
GitHubCommitNotifier_SettingCommitStatus(repository.getHtmlUrl() + "/commit/" + sha1)
144-
);
145-
146-
try {
147-
repository.createCommitStatus(
148-
sha1, status.getState(), build.getAbsoluteUrl(),
149-
message,
150-
contextName
151-
);
152-
} catch (FileNotFoundException e) {
153-
// PR builds and other merge activities can create a merge commit that
154-
// doesn't exist in the upstream. Don't let the build fail
155-
// TODO: ideally we'd like other plugins to designate a commit to put the status update to
156-
LOGGER.debug("Failed to update commit status", e);
157-
listener.getLogger()
158-
.format("Commit doesn't exist in %s. Status is not set%n", repository.getFullName());
159-
}
160-
}
161-
}
162-
}
163-
164-
private static StatusResult statusFrom(@Nonnull Run<?, ?> build) {
165-
Result result = build.getResult();
166-
167-
// We do not use `build.getDurationString()` because it appends 'and counting' (build is still running)
168-
String duration = Util.getTimeSpanString(System.currentTimeMillis() - build.getTimeInMillis());
169-
170-
if (result == null) { // Build is ongoing
171-
return new StatusResult(
172-
GHCommitState.PENDING,
173-
Messages.CommitNotifier_Pending(build.getDisplayName())
174-
);
175-
} else if (result.isBetterOrEqualTo(SUCCESS)) {
176-
return new StatusResult(
177-
GHCommitState.SUCCESS,
178-
Messages.CommitNotifier_Success(build.getDisplayName(), duration)
179-
);
180-
} else if (result.isBetterOrEqualTo(UNSTABLE)) {
181-
return new StatusResult(
182-
GHCommitState. 10000 FAILURE,
183-
Messages.CommitNotifier_Unstable(build.getDisplayName(), duration)
184-
);
116+
public void perform(@NonNull Run<?, ?> build,
117+
@NonNull FilePath ws,
118+
@NonNull Launcher launcher,
119+
@NonNull TaskListener listener) throws InterruptedException, IOException {
120+
121+
GitHubCommitStatusSetter setter = new GitHubCommitStatusSetter();
122+
setter.setReposSource(new AnyDefinedRepositorySource());
123+
setter.setCommitShaSource(new BuildDataRevisionShaSource());
124+
setter.setContextSource(new DefaultCommitContextSource());
125+
126+
127+
String content = firstNonNull(statusMessage, DEFAULT_MESSAGE).getContent();
128+
129+
if (isNotBlank(content)) {
130+
setter.setStatusResultSource(new ConditionalStatusResultSource(
131+
asList(
132+
betterThanOrEqualTo(SUCCESS, GHCommitState.SUCCESS, content),
133+
betterThanOrEqualTo(UNSTABLE, GHCommitState.FAILURE, content),
134+
betterThanOrEqualTo(FAILURE, GHCommitState.ERROR, content),
135+
onAnyResult(GHCommitState.PENDING, content)
136+
)));
185137
} else {
186-
return new StatusResult(
187-
GHCommitState.ERROR,
188-
Messages.CommitNotifier_Failed(build.getDisplayName(), duration)
189-
);
138+
setter.setStatusResultSource(new DefaultStatusResultSource());
190139
}
191-
}
192140

193-
private static class StatusResult {
194-
private GHCommitState state;
195-
private String msg;
196-
197-
public StatusResult(GHCommitState state, String msg) {
198-
this.state = state;
199-
this.msg = msg;
200-
}
201-
202-
public GHCommitState getState() {
203-
return state;
141+
if (getEffectiveResultOnFailure().equals(SUCCESS)) {
142+
setter.setErrorHandlers(Collections.<StatusErrorHandler>singletonList(new ShallowAnyErrorHandler()));
143+
} else if (resultOnFailure == null) {
144+
setter.setErrorHandlers(null);
145+
} else {
146+
setter.setErrorHandlers(Collections.<StatusErrorHandler>singletonList(
147+
new ChangingBuildStatusErrorHandler(getEffectiveResultOnFailure().toString())));
204148
}
205149

206-
public String getMsg() {
207-
return msg;
208-
}
150+
setter.perform(build, ws, launcher, listener);
209151
}
210152

211153
@Extension

src/main/java/com/cloudbees/jenkins/GitHubSetCommitStatusBuilder.java

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.cloudbees.jenkins;
22

3+
import edu.umd.cs.findbugs.annotations.NonNull;
34
import hudson.Extension;
45
import hudson.FilePath;
56
import hudson.Launcher;
@@ -9,21 +10,25 @@
910
import hudson.tasks.BuildStepDescriptor;
1011
import hudson.tasks.Builder;
1112
import jenkins.tasks.SimpleBuildStep;
12-
13-
import org.eclipse.jgit.lib.ObjectId;
1413
import org.jenkinsci.plugins.github.common.ExpandableMessage;
15-
import org.jenkinsci.plugins.github.util.BuildDataHelper;
14+
import org.jenkinsci.plugins.github.extension.status.StatusErrorHandler;
15+
import org.jenkinsci.plugins.github.extension.status.misc.ConditionalResult;
16+
import org.jenkinsci.plugins.github.status.GitHubCommitStatusSetter;
17+
import org.jenkinsci.plugins.github.status.err.ShallowAnyErrorHandler;
18+
import org.jenkinsci.plugins.github.status.sources.AnyDefinedRepositorySource;
19+
import org.jenkinsci.plugins.github.status.sources.BuildDataRevisionShaSource;
20+
import org.jenkinsci.plugins.github.status.sources.ConditionalStatusResultSource;
21+
import org.jenkinsci.plugins.github.status.sources.DefaultCommitContextSource;
1622
import org.kohsuke.github.GHCommitState;
17-
import org.kohsuke.github.GHRepository;
1823
import org.kohsuke.stapler.DataBoundConstructor;
1924
import org.kohsuke.stapler.DataBoundSetter;
2025

2126
import java.io.IOException;
27+
import java.util.Collections;
2228

23-
import static com.cloudbees.jenkins.Messages.GitHubCommitNotifier_SettingCommitStatus;
24-
import static com.coravy.hudson.plugins.github.GithubProjectProperty.displayNameFor;
2529
import static com.google.common.base.Objects.firstNonNull;
2630
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
31+
import static org.jenkinsci.plugins.github.status.sources.misc.AnyBuildResult.onAnyResult;
2732

2833
@Extension
2934
public class GitHubSetCommitStatusBuilder extends Builder implements SimpleBuildStep {
@@ -51,29 +56,27 @@ public void setStatusMessage(ExpandableMessage statusMessage) {
5156
}
5257

5358
@Override
54-
public void perform(Run<?, ?> build,
55-
FilePath workspace,
56-
Launcher launcher,
57-
TaskListener listener) throws InterruptedException, IOException {
58-
final String sha1 = ObjectId.toString(BuildDataHelper.getCommitSHA1(build));
59-
String message = defaultIfEmpty(
60-
firstNonNull(statusMessage, DEFAULT_MESSAGE).expandAll(build, listener),
61-
Messages.CommitNotifier_Pending(build.getDisplayName())
62-
);
63-
String contextName = displayNameFor(build.getParent());
59+
public void perform(@NonNull Run<?, ?> build,
60+
@NonNull FilePath workspace,
61+
@NonNull Launcher launcher,
62+
@NonNull TaskListener listener) throws InterruptedException, IOException {
6463

65-
for (GitHubRepositoryName name : GitHubRepositoryNameContributor.parseAssociatedNames(build.getParent())) {
66-
for (GHRepository repository : name.resolve()) {
67-
listener.getLogger().println(
68-
GitHubCommitNotifier_SettingCommitStatus(repository.getHtmlUrl() + "/commit/" + sha1)
69-
);
70-
repository.createCommitStatus(sha1,
71-
GHCommitState.PENDING,
72-
build.getAbsoluteUrl(),
73-
message,
74-
contextName);
75-
}
76-
}
64+
GitHubCommitStatusSetter setter = new GitHubCommitStatusSetter();
65+
setter.setReposSource(new AnyDefinedRepositorySource());
66+
setter.setCommitShaSource(new BuildDataRevisionShaSource());
67+
setter.setContextSource(new DefaultCommitContextSource());
68+
setter.setErrorHandlers(Collections.<StatusErrorHandler>singletonList(new ShallowAnyErrorHandler()));
69+
70+
setter.setStatusResultSource(new ConditionalStatusResultSource(
71+
Collections.<ConditionalResult>singletonList(
72+
onAnyResult(
73+
GHCommitState.PENDING,
74+
defaultIfEmpty(firstNonNull(statusMessage, DEFAULT_MESSAGE).getContent(),
75+
Messages.CommitNotifier_Pending(build.getDisplayName()))
76+
)
77+
)));
78+
79+
setter.perform(build, workspace, launcher, listener);
7780
}
7881

7982
@Extension
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.jenkinsci.plugins.github.common;
2+
3+
import hudson.model.Run;
4+
import hudson.model.TaskListener;
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import javax.annotation.Nonnull;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
13+
14+
/**
15+
* With help of list of other error handlers handles exception.
16+
* If no one will handle it, exception will be wrapped to {@link ErrorHandlingException}
17+
* and thrown by the handle method
18+
*
19+
* @author lanwen (Merkushev Kirill)
20+
* @since 1.19.0
21+
*/
22+
public class CombineErrorHandler implements ErrorHandler {
23+
private static final Logger LOG = LoggerFactory.getLogger(CombineErrorHandler.class);
24+
25+
private List<ErrorHandler> handlers = new ArrayList<>();
26+
27+
private CombineErrorHandler() {
28+
}
29+
30+
/**
31+
* Static factory to produce new instance of this handler
32+
*
33+
* @return new instance
34+
*/
35+
public static CombineErrorHandler errorHandling() {
36+
return new CombineErrorHandler();
37+
}
38+
39+
public CombineErrorHandler withHandlers(List<? extends ErrorHandler> handlers) {
40+
if (isNotEmpty(handlers)) {
41+
this.handlers.addAll(handlers);
42+
}
43+
return this;
44+
}
45+
46+
/**
47+
* Handles exception with help of other handlers. If no one will handle it, it will be thrown to the top level
48+
*
49+
* @param e exception to handle (log, ignore, process, rethrow)
50+
* @param run run object from the step
51+
* @param listener listener object from the step
52+
*
53+
* @return true if exception handled or rethrows it
54+
*/
55+
@Override
56+
public boolean handle(Exception e, @Nonnull Run<?, ?> run, @Nonnull TaskListener listener) {
57+
LOG.debug("Exception in {} will be processed with {} handlers",
58+
run.getParent().getName(), handlers.size(), e);
59+
try {
60+
for (ErrorHandler next : handlers) {
61+
if (next.handle(e, run, listener)) {
62+
LOG.debug("Exception in {} [{}] handled by [{}]",
63+
run.getParent().getName(),
64+
e.getMessage(),
65+
next.getClass());
66+
return true;
67+
}
68+
}
69+
} catch (Exception unhandled) {
70+
LOG.error("Exception in {} unhandled", run.getParent().getName(), unhandled);
71+
throw new ErrorHandlingException(unhandled);
72+
}
73+
74+
throw new ErrorHandlingException(e);
75+
}
76+
77+
/**
78+
* Wrapper for the not handled by this handler exceptions
79+
*/
80+
public static class ErrorHandlingException extends RuntimeException {
81+
public ErrorHandlingException(Throwable cause) {
82+
super(cause);
83+
}
84+
}
85+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.jenkinsci.plugins.github.common;
2+
3+
import hudson.model.Run;
4+
import hudson.model.TaskListener;
5+
6+
import javax.annotation.Nonnull;
7+
8+
/**
9+
* So you can implement bunch of {@link ErrorHandler}s and log, rethrow, ignore exception.
10+
* Useful to control own step exceptions
11+
* (for example {@link org.jenkinsci.plugins.github.status.GitHubCommitStatusSetter})
12+
*
13+
* @author lanwen (Merkushev Kirill)
14+
* @since 1.19.0
15+
*/
16+
public interface ErrorHandler {
17+
18+
/**
19+
* Normally should return true if exception is handled and no other handler should do anything.
20+
* If you will return false, the next error handler should try to handle this exception
21+
*
22+
* @param e exception to handle (log, ignore, process, rethrow)
23+
* @param run run object from the step
24+
* @param listener listener object from the step
25+
*
26+
* @return true if exception handled successfully
27+
* @throws Exception you can rethrow exception of any type
28+
*/
29+
boolean handle(Exception e, @Nonnull Run<?, ?> run, @Nonnull TaskListener listener) throws Exception;
30+
}

0 commit comments

Comments
 (0)
0