mirror of
https://github.com/theonedev/onedev.git
synced 2025-12-08 18:26:30 +00:00
Build subscription
This commit is contained in:
parent
5f2a4590c5
commit
1e44c20eb3
@ -169,6 +169,7 @@ import io.onedev.server.migration.PersistentBagConverter;
|
||||
import io.onedev.server.model.support.administration.authenticator.Authenticator;
|
||||
import io.onedev.server.model.support.administration.jobexecutor.AutoDiscoveredJobExecutor;
|
||||
import io.onedev.server.model.support.administration.jobexecutor.JobExecutor;
|
||||
import io.onedev.server.notification.BuildNotificationManager;
|
||||
import io.onedev.server.notification.CodeCommentNotificationManager;
|
||||
import io.onedev.server.notification.CommitNotificationManager;
|
||||
import io.onedev.server.notification.DefaultMailManager;
|
||||
@ -340,6 +341,7 @@ public class CoreModule extends AbstractPluginModule {
|
||||
bind(WorkExecutor.class).to(DefaultWorkExecutor.class);
|
||||
bind(PullRequestNotificationManager.class);
|
||||
bind(CommitNotificationManager.class);
|
||||
bind(BuildNotificationManager.class);
|
||||
bind(IssueNotificationManager.class);
|
||||
bind(EntityReferenceManager.class);
|
||||
bind(CodeCommentNotificationManager.class);
|
||||
|
||||
@ -57,8 +57,6 @@ import io.onedev.server.ci.job.log.LogManager;
|
||||
import io.onedev.server.ci.job.param.JobParam;
|
||||
import io.onedev.server.ci.job.paramspec.ParamSpec;
|
||||
import io.onedev.server.ci.job.paramspec.SecretParam;
|
||||
import io.onedev.server.ci.job.retry.JobRetry;
|
||||
import io.onedev.server.ci.job.retry.RetryCondition;
|
||||
import io.onedev.server.ci.job.trigger.JobTrigger;
|
||||
import io.onedev.server.entitymanager.BuildManager;
|
||||
import io.onedev.server.entitymanager.BuildParamManager;
|
||||
@ -554,6 +552,7 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
|
||||
build.setRunningDate(null);
|
||||
build.setSubmitDate(new Date());
|
||||
build.setSubmitter(submitter);
|
||||
build.setRetried(0);
|
||||
buildParamManager.deleteParams(build);
|
||||
for (Map.Entry<String, List<String>> entry: paramMap.entrySet()) {
|
||||
ParamSpec paramSpec = build.getJob().getParamSpecMap().get(entry.getKey());
|
||||
@ -724,6 +723,7 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
|
||||
@Listen
|
||||
public void on(BuildSubmitted event) {
|
||||
Build build = event.getBuild();
|
||||
build.setWillRetry(false);
|
||||
FileUtils.deleteDir(build.getPublishDir());
|
||||
}
|
||||
|
||||
@ -731,21 +731,17 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
|
||||
@Listen
|
||||
public void on(BuildFinished event) {
|
||||
Build build = event.getBuild();
|
||||
JobRetry retry = build.getJob().getRetry();
|
||||
build.setWillRetry(build.getStatus() == Build.Status.FAILED
|
||||
&& retry != null
|
||||
&& build.getRetried() < retry.getMaxRetries()
|
||||
&& RetryCondition.parse(retry.getRetryCondition()).satisfied(build));
|
||||
|
||||
if (build.isWillRetry()) {
|
||||
build.setWillRetry(build.willRetryNow());
|
||||
if (build.willRetryNow()) {
|
||||
Long buildId = build.getId();
|
||||
int retried = build.getRetried();
|
||||
int retryDelay = build.getJob().getRetry().getRetryDelay();
|
||||
transactionManager.runAsyncAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(retry.getRetryDelay() * (long)(Math.pow(2, retried)) * 1000L);
|
||||
Thread.sleep(retryDelay * (long)(Math.pow(2, retried)) * 1000L);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -762,6 +758,8 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
|
||||
build.setPendingDate(null);
|
||||
build.setRunningDate(null);
|
||||
build.setSubmitDate(new Date());
|
||||
build.setCanceller(null);
|
||||
build.setCancellerName(null);
|
||||
buildManager.save(build);
|
||||
listenerRegistry.post(new BuildSubmitted(build));
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import javax.annotation.Nullable;
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.CodeComment;
|
||||
import io.onedev.server.model.CodeCommentReply;
|
||||
import io.onedev.server.model.Issue;
|
||||
@ -28,6 +29,8 @@ public interface UrlManager {
|
||||
|
||||
String urlFor(Issue issue);
|
||||
|
||||
String urlFor(Build build);
|
||||
|
||||
String urlFor(IssueComment comment);
|
||||
|
||||
String urlFor(IssueChange change);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package io.onedev.server.entitymanager.impl;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@ -35,7 +37,11 @@ public class DefaultBuildQuerySettingManager extends AbstractEntityManager<Build
|
||||
@Transactional
|
||||
@Override
|
||||
public void save(BuildQuerySetting setting) {
|
||||
if (setting.getUserQueries().isEmpty()) {
|
||||
setting.getQuerySubscriptionSupport().getUserQuerySubscriptions().retainAll(
|
||||
setting.getUserQueries().stream().map(it->it.getName()).collect(Collectors.toSet()));
|
||||
setting.getQuerySubscriptionSupport().getProjectQuerySubscriptions().retainAll(
|
||||
setting.getProject().getSavedBuildQueries().stream().map(it->it.getName()).collect(Collectors.toSet()));
|
||||
if (setting.getQuerySubscriptionSupport().getProjectQuerySubscriptions().isEmpty() && setting.getUserQueries().isEmpty()) {
|
||||
if (!setting.isNew())
|
||||
delete(setting);
|
||||
} else {
|
||||
|
||||
@ -57,6 +57,8 @@ import io.onedev.server.ci.job.VariableInterpolator;
|
||||
import io.onedev.server.ci.job.param.JobParam;
|
||||
import io.onedev.server.ci.job.paramspec.ParamSpec;
|
||||
import io.onedev.server.ci.job.paramspec.SecretParam;
|
||||
import io.onedev.server.ci.job.retry.JobRetry;
|
||||
import io.onedev.server.ci.job.retry.RetryCondition;
|
||||
import io.onedev.server.git.GitUtils;
|
||||
import io.onedev.server.git.RefInfo;
|
||||
import io.onedev.server.model.support.inputspec.SecretInput;
|
||||
@ -153,6 +155,8 @@ public class Build extends AbstractEntity implements Referenceable {
|
||||
|
||||
private boolean willRetry;
|
||||
|
||||
private transient Boolean willRetryNow;
|
||||
|
||||
@Column(length=MAX_STATUS_MESSAGE_LEN)
|
||||
private String statusMessage;
|
||||
|
||||
@ -326,6 +330,17 @@ public class Build extends AbstractEntity implements Referenceable {
|
||||
public void setWillRetry(boolean willRetry) {
|
||||
this.willRetry = willRetry;
|
||||
}
|
||||
|
||||
public boolean willRetryNow() {
|
||||
if (willRetryNow == null) {
|
||||
JobRetry retry = getJob().getRetry();
|
||||
willRetryNow = getStatus() == Build.Status.FAILED
|
||||
&& retry != null
|
||||
&& getRetried() < retry.getMaxRetries()
|
||||
&& RetryCondition.parse(retry.getRetryCondition()).satisfied(this);
|
||||
}
|
||||
return willRetryNow;
|
||||
}
|
||||
|
||||
public Collection<BuildParam> getParams() {
|
||||
return params;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package io.onedev.server.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
@ -38,6 +39,14 @@ public class BuildQuerySetting extends QuerySetting<NamedBuildQuery> {
|
||||
@Column(nullable=false, length=65535)
|
||||
private ArrayList<NamedBuildQuery> userQueries = new ArrayList<>();
|
||||
|
||||
@Lob
|
||||
@Column(nullable=false, length=65535)
|
||||
private LinkedHashSet<String> userQuerySubscriptions = new LinkedHashSet<>();
|
||||
|
||||
@Lob
|
||||
@Column(nullable=false, length=65535)
|
||||
private LinkedHashSet<String> projectQuerySubscriptions = new LinkedHashSet<>();
|
||||
|
||||
@Override
|
||||
public Project getProject() {
|
||||
return project;
|
||||
@ -73,7 +82,19 @@ public class BuildQuerySetting extends QuerySetting<NamedBuildQuery> {
|
||||
|
||||
@Override
|
||||
public QuerySubscriptionSupport<NamedBuildQuery> getQuerySubscriptionSupport() {
|
||||
return null;
|
||||
return new QuerySubscriptionSupport<NamedBuildQuery>() {
|
||||
|
||||
@Override
|
||||
public LinkedHashSet<String> getUserQuerySubscriptions() {
|
||||
return userQuerySubscriptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashSet<String> getProjectQuerySubscriptions() {
|
||||
return projectQuerySubscriptions;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -298,6 +298,7 @@ public class Project extends AbstractEntity {
|
||||
savedBuildQueries.add(new NamedBuildQuery("All", "all"));
|
||||
savedBuildQueries.add(new NamedBuildQuery("Successful", "successful"));
|
||||
savedBuildQueries.add(new NamedBuildQuery("Failed", "failed"));
|
||||
savedBuildQueries.add(new NamedBuildQuery("Failed eventually", "failed and not(will retry)"));
|
||||
savedBuildQueries.add(new NamedBuildQuery("In error", "in error"));
|
||||
savedBuildQueries.add(new NamedBuildQuery("Cancelled", "cancelled"));
|
||||
savedBuildQueries.add(new NamedBuildQuery("Timed out", "timed out"));
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
package io.onedev.server.notification;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.onedev.commons.launcher.loader.Listen;
|
||||
import io.onedev.server.entitymanager.UrlManager;
|
||||
import io.onedev.server.event.build.BuildEvent;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.BuildQuerySetting;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.NamedQuery;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.search.entity.build.BuildQuery;
|
||||
|
||||
@Singleton
|
||||
public class BuildNotificationManager {
|
||||
|
||||
private final MailManager mailManager;
|
||||
|
||||
private final UrlManager urlManager;
|
||||
|
||||
@Inject
|
||||
public BuildNotificationManager(MailManager mailManager, UrlManager urlManager) {
|
||||
this.mailManager = mailManager;
|
||||
this.urlManager = urlManager;
|
||||
}
|
||||
|
||||
private void fillSubscribedQueryStrings(Map<User, Collection<String>> subscribedQueryStrings, User user, @Nullable NamedQuery query) {
|
||||
if (query != null) {
|
||||
Collection<String> value = subscribedQueryStrings.get(user);
|
||||
if (value == null) {
|
||||
value = new HashSet<>();
|
||||
subscribedQueryStrings.put(user, value);
|
||||
}
|
||||
value.add(query.getQuery());
|
||||
}
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(BuildEvent event) {
|
||||
Project project = event.getProject();
|
||||
Map<User, Collection<String>> subscribedQueryStrings = new HashMap<>();
|
||||
for (BuildQuerySetting setting: project.getBuildQuerySettings()) {
|
||||
for (String queryName: setting.getQuerySubscriptionSupport().getProjectQuerySubscriptions())
|
||||
fillSubscribedQueryStrings(subscribedQueryStrings, setting.getUser(), project.getSavedBuildQuery(queryName));
|
||||
for (String queryName: setting.getQuerySubscriptionSupport().getUserQuerySubscriptions())
|
||||
fillSubscribedQueryStrings(subscribedQueryStrings, setting.getUser(), setting.getUserQuery(queryName));
|
||||
}
|
||||
|
||||
Build build = event.getBuild();
|
||||
Collection<String> notifyEmails = new HashSet<>();
|
||||
for (Map.Entry<User, Collection<String>> entry: subscribedQueryStrings.entrySet()) {
|
||||
User user = entry.getKey();
|
||||
for (String queryString: entry.getValue()) {
|
||||
try {
|
||||
if (BuildQuery.parse(event.getProject(), queryString).matches(build, user)) {
|
||||
notifyEmails.add(user.getEmail());
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String subject;
|
||||
if (build.getVersion() != null) {
|
||||
subject = String.format("Build %s/%s/#%s (%s) is %s", build.getProject().getName(), build.getJobName(),
|
||||
build.getNumber(), build.getVersion(), build.getStatus().getDisplayName().toLowerCase());
|
||||
} else {
|
||||
subject = String.format("Build %s/%s/#%s is %s", build.getProject().getName(), build.getJobName(),
|
||||
build.getNumber(), build.getStatus().getDisplayName().toLowerCase());
|
||||
}
|
||||
String url = urlManager.urlFor(build);
|
||||
String body = String.format("Visit <a href='%s'>%s</a> for details", url, url);
|
||||
mailManager.sendMailAsync(notifyEmails, subject, body.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@ -23,7 +23,7 @@ public class WillRetryCriteria extends EntityCriteria<Build> {
|
||||
|
||||
@Override
|
||||
public boolean matches(Build build, User user) {
|
||||
return build.isWillRetry();
|
||||
return build.willRetryNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -13,6 +13,7 @@ import com.google.common.base.Splitter;
|
||||
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UrlManager;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.CodeComment;
|
||||
import io.onedev.server.model.CodeCommentReply;
|
||||
import io.onedev.server.model.Issue;
|
||||
@ -98,9 +99,14 @@ public class DefaultUrlManager implements UrlManager {
|
||||
|
||||
@Override
|
||||
public String urlFor(Issue issue) {
|
||||
return urlFor(issue.getProject()) + "/issues/" + issue.getNumber() + "/activities";
|
||||
return urlFor(issue.getProject()) + "/issues/" + issue.getNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String urlFor(Build build) {
|
||||
return urlFor(build.getProject()) + "/builds/" + build.getNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String urlFor(IssueComment comment) {
|
||||
return urlFor(comment.getIssue()) + "#" + comment.getAnchor();
|
||||
|
||||
@ -235,6 +235,7 @@ public class OneUrlMapper extends CompoundRequestMapper {
|
||||
add(new OnePageMapper("projects/${project}/issue-boards", IssueBoardsPage.class));
|
||||
add(new OnePageMapper("projects/${project}/issue-boards/${board}", IssueBoardsPage.class));
|
||||
add(new OnePageMapper("projects/${project}/issue-list", IssueListPage.class));
|
||||
add(new OnePageMapper("projects/${project}/issues/${issue}", IssueActivitiesPage.class));
|
||||
add(new OnePageMapper("projects/${project}/issues/${issue}/activities", IssueActivitiesPage.class));
|
||||
add(new OnePageMapper("projects/${project}/issues/${issue}/builds", FixingBuildsPage.class));
|
||||
add(new OnePageMapper("projects/${project}/issues/new", NewIssuePage.class));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user