Display job information on commit detail page

This commit is contained in:
Robin Shen 2019-05-26 17:32:07 +08:00
parent d7ae03ad21
commit b4bccc3dc7
74 changed files with 989 additions and 668 deletions

View File

@ -81,9 +81,9 @@ import io.onedev.server.cache.DefaultCommitInfoManager;
import io.onedev.server.cache.DefaultUserInfoManager;
import io.onedev.server.cache.UserInfoManager;
import io.onedev.server.ci.detector.CISpecDetector;
import io.onedev.server.ci.job.DefaultJobScheduler;
import io.onedev.server.ci.job.DefaultJobManager;
import io.onedev.server.ci.job.DependencyPopulator;
import io.onedev.server.ci.job.JobScheduler;
import io.onedev.server.ci.job.JobManager;
import io.onedev.server.ci.job.log.DefaultLogManager;
import io.onedev.server.ci.job.log.LogManager;
import io.onedev.server.ci.job.log.instruction.LogInstruction;
@ -109,7 +109,7 @@ import io.onedev.server.entitymanager.IssueWatchManager;
import io.onedev.server.entitymanager.MembershipManager;
import io.onedev.server.entitymanager.MilestoneManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.BuildRequirementManager;
import io.onedev.server.entitymanager.PullRequestBuildManager;
import io.onedev.server.entitymanager.PullRequestChangeManager;
import io.onedev.server.entitymanager.PullRequestCommentManager;
import io.onedev.server.entitymanager.PullRequestManager;
@ -142,7 +142,7 @@ import io.onedev.server.entitymanager.impl.DefaultIssueWatchManager;
import io.onedev.server.entitymanager.impl.DefaultMembershipManager;
import io.onedev.server.entitymanager.impl.DefaultMilestoneManager;
import io.onedev.server.entitymanager.impl.DefaultProjectManager;
import io.onedev.server.entitymanager.impl.DefaultBuildRequirementManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestBuildManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestChangeManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestCommentManager;
import io.onedev.server.entitymanager.impl.DefaultPullRequestManager;
@ -315,9 +315,9 @@ public class CoreModule extends AbstractPluginModule {
bind(PullRequestReviewManager.class).to(DefaultPullRequestReviewManager.class);
bind(BuildManager.class).to(DefaultBuildManager.class);
bind(BuildDependenceManager.class).to(DefaultBuildDependenceManager.class);
bind(JobScheduler.class).to(DefaultJobScheduler.class);
bind(JobManager.class).to(DefaultJobManager.class);
bind(LogManager.class).to(DefaultLogManager.class);
bind(BuildRequirementManager.class).to(DefaultBuildRequirementManager.class);
bind(PullRequestBuildManager.class).to(DefaultPullRequestBuildManager.class);
bind(MailManager.class).to(DefaultMailManager.class);
bind(IssueManager.class).to(DefaultIssueManager.class);
bind(IssueFieldManager.class).to(DefaultIssueFieldManager.class);

View File

@ -133,13 +133,13 @@ public class DefaultCacheManager implements CacheManager {
for (Membership membership: dao.query(Membership.class))
memberships.put(membership.getId(), membership.getFacade());
Query<?> query = dao.getSessionManager().getSession().createQuery("select id, project.id, commitHash from Build");
Query<?> query = dao.getSession().createQuery("select id, project.id, commitHash from Build");
for (Object[] fields: (List<Object[]>)query.list()) {
Long buildId = (Long) fields[0];
builds.put(buildId, new BuildFacade(buildId, (Long)fields[1], (String)fields[2]));
}
query = dao.getSessionManager().getSession().createQuery("select id, project.id, number from Issue");
query = dao.getSession().createQuery("select id, project.id, number from Issue");
for (Object[] fields: (List<Object[]>)query.list()) {
Long issueId = (Long) fields[0];
issues.put(issueId, new IssueFacade(issueId, (Long)fields[1], (Long)fields[2]));
@ -150,7 +150,7 @@ public class DefaultCacheManager implements CacheManager {
for (UserAuthorization userAuthorization: dao.query(UserAuthorization.class))
userAuthorizations.put(userAuthorization.getId(), userAuthorization.getFacade());
query = dao.getSessionManager().getSession().createQuery("select distinct name, type, value, id from BuildParam order by id");
query = dao.getSession().createQuery("select distinct name, type, value, id from BuildParam order by id");
for (Object[] fields: (List<Object[]>)query.list()) {
if (!fields[1].equals(InputSpec.SECRET))
addBuildParam((String) fields[0], (String) fields[2]);

View File

@ -1,5 +1,7 @@
package io.onedev.server.ci;
import static java.util.stream.Collectors.toSet;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
@ -13,8 +15,11 @@ import javax.validation.ConstraintValidatorContext;
import javax.validation.ValidationException;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import io.onedev.commons.utils.DependencyAware;
import io.onedev.commons.utils.DependencyUtils;
import io.onedev.commons.utils.StringUtils;
import io.onedev.server.ci.job.Job;
import io.onedev.server.ci.job.param.JobParam;
@ -35,6 +40,8 @@ public class CISpec implements Serializable, Validatable {
private List<Job> jobs = new ArrayList<>();
private transient Map<String, Job> jobMap;
private transient List<Job> sortedJobs;
@Editable
public List<Job> getJobs() {
@ -53,6 +60,37 @@ public class CISpec implements Serializable, Validatable {
}
return jobMap;
}
/**
* Get jobs sorted by dependencies
*
* @return
*/
public List<Job> getSortedJobs() {
if (sortedJobs == null) {
sortedJobs = new ArrayList<>();
Map<String, DependencyAware<String>> dependencyMap = new LinkedHashMap<>();
for (Job job: jobs) {
dependencyMap.put(job.getName(), new DependencyAware<String>() {
@Override
public String getId() {
return job.getName();
}
@Override
public Set<String> getDependencies() {
return job.getDependencies().stream().map(it->it.getJobName()).collect(toSet());
}
});
}
for (String jobName: DependencyUtils.sortDependencies(dependencyMap))
sortedJobs.add(Preconditions.checkNotNull(getJobMap().get(jobName)));
}
return sortedJobs;
}
@Override
public boolean isValid(ConstraintValidatorContext context) {

View File

@ -1,11 +1,11 @@
package io.onedev.server.ci.job;
import java.io.File;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@ -16,10 +16,12 @@ import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Lock;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.transaction.Synchronization;
import javax.validation.ValidationException;
import org.eclipse.jgit.lib.ObjectId;
@ -31,7 +33,6 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.CharMatcher;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import io.onedev.commons.launcher.loader.Listen;
import io.onedev.commons.launcher.loader.ListenerRegistry;
@ -49,7 +50,6 @@ import io.onedev.server.ci.job.param.JobParam;
import io.onedev.server.ci.job.trigger.JobTrigger;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.BuildParamManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.entitymanager.UserManager;
import io.onedev.server.event.BuildCommitAware;
@ -81,18 +81,16 @@ import io.onedev.server.util.inputspec.SecretInput;
import io.onedev.server.util.patternset.PatternSet;
@Singleton
public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableTask {
public class DefaultJobManager implements JobManager, Runnable, SchedulableTask {
private static final int CHECK_INTERVAL = 1000; // check internal in milli-seconds
private static final Logger logger = LoggerFactory.getLogger(DefaultJobScheduler.class);
private static final Logger logger = LoggerFactory.getLogger(DefaultJobManager.class);
private enum Status {STARTED, STOPPING, STOPPED};
private final Map<Long, JobExecution> jobExecutions = new ConcurrentHashMap<>();
private final ProjectManager projectManager;
private final BuildManager buildManager;
private final UserManager userManager;
@ -122,12 +120,11 @@ public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableT
private volatile Status status;
@Inject
public DefaultJobScheduler(ProjectManager projectManager, BuildManager buildManager,
public DefaultJobManager(BuildManager buildManager,
UserManager userManager, ListenerRegistry listenerRegistry, SettingManager settingManager,
TransactionManager transactionManager, LogManager logManager, ExecutorService executorService,
SessionManager sessionManager, Set<DependencyPopulator> dependencyPopulators,
TaskScheduler taskScheduler, BuildParamManager buildParamManager) {
this.projectManager = projectManager;
this.settingManager = settingManager;
this.buildManager = buildManager;
this.userManager = userManager;
@ -141,54 +138,32 @@ public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableT
this.buildParamManager = buildParamManager;
}
@Sessional
@Transactional
@Override
public void submit(Project project, String commitHash, String jobName, Map<String, List<List<String>>> paramMatrix) {
String lockKey = "job-schedule: " + project.getId() + "-" + commitHash;
Long projectId = project.getId();
Long submitterId = User.idOf(userManager.getCurrent());
transactionManager.runAsyncAfterCommit(new Runnable() {
public Build submit(Project project, String commitHash, String jobName, Map<String, List<String>> paramMap) {
Lock lock = LockUtils.getLock("job-schedule: " + project.getId() + "-" + commitHash);
transactionManager.getTransaction().registerSynchronization(new Synchronization() {
@Override
public void run() {
LockUtils.call(lockKey, new Callable<Void>() {
@Override
public Void call() {
transactionManager.run(new Runnable() {
@Override
public void run() {
Project project = projectManager.load(projectId);
User submitter = (submitterId != null? userManager.load(submitterId): null);
new MatrixRunner<List<String>>(paramMatrix) {
@Override
public void run(Map<String, List<String>> params) {
submit(project, submitter, commitHash, jobName, params, new LinkedHashSet<>());
}
}.run();
}
});
return null;
}
});
public void beforeCompletion() {
}
@Override
public void afterCompletion(int status) {
lock.unlock();
}
});
try {
lock.lockInterruptibly();
return submit(project, commitHash, jobName, paramMap, new LinkedHashSet<>());
} catch (Exception e) {
throw ExceptionUtils.unchecked(e);
}
}
private Map<String, List<List<String>>> getParamMatrix(List<JobParam> params) {
Map<String, List<List<String>>> paramMatrix = new LinkedHashMap<>();
for (JobParam param: params)
paramMatrix.put(param.getName(), param.getValuesProvider().getValues());
return paramMatrix;
}
private List<Build> submit(Project project, @Nullable User user, String commitHash, String jobName,
private Build submit(Project project, String commitHash, String jobName,
Map<String, List<String>> paramMap, Set<String> checkedJobNames) {
Build build = new Build();
build.setProject(project);
@ -196,30 +171,30 @@ public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableT
build.setJobName(jobName);
build.setSubmitDate(new Date());
build.setStatus(Build.Status.WAITING);
build.setSubmitter(user);
build.setSubmitter(userManager.getCurrent());
try {
JobParam.validateParams(build.getJob().getParamSpecs(), JobParam.getParamMatrix(paramMap));
JobParam.validateParamMap(build.getJob().getParamSpecMap(), paramMap);
} catch (ValidationException e) {
markBuildError(build, e.getMessage());
return Lists.newArrayList(build);
String message = String.format("Error validating build parameters (project: %s, commit: %s, job: %s)",
project.getName(), commitHash, jobName);
throw new OneException(message, e);
}
if (!checkedJobNames.add(jobName)) {
markBuildError(build, "Circular job dependencies found: " + checkedJobNames);
return Lists.newArrayList(build);
String message = String.format("Circular job dependencies found (project: %s, commit: %s, job: %s, dependency loop: %s)",
project.getName(), commitHash, jobName, checkedJobNames);
throw new OneException(message);
}
Map<String, List<String>> copyOfParamMap = new HashMap<>(paramMap);
Map<String, List<String>> paramMapToQuery = new HashMap<>(paramMap);
for (InputSpec paramSpec: build.getJob().getParamSpecs()) {
if (paramSpec instanceof SecretInput)
copyOfParamMap.remove(paramSpec.getName());
paramMapToQuery.remove(paramSpec.getName());
}
List<Build> builds = buildManager.query(project, commitHash, jobName, copyOfParamMap);
Collection<Build> builds = buildManager.query(project, commitHash, jobName, paramMapToQuery);
if (builds.isEmpty()) {
builds.add(build);
for (Map.Entry<String, List<String>> entry: paramMap.entrySet()) {
InputSpec paramSpec = Preconditions.checkNotNull(build.getJob().getParamSpecMap().get(entry.getKey()));
if (!entry.getValue().isEmpty()) {
@ -241,18 +216,16 @@ public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableT
}
for (JobDependency dependency: build.getJob().getDependencies()) {
new MatrixRunner<List<String>>(getParamMatrix(dependency.getJobParams())) {
new MatrixRunner<List<String>>(JobParam.getParamMatrix(dependency.getJobParams())) {
@Override
public void run(Map<String, List<String>> params) {
List<Build> dependencyBuilds = submit(project, null, commitHash, dependency.getJobName(),
Build dependencyBuild = submit(project, commitHash, dependency.getJobName(),
params, new LinkedHashSet<>(checkedJobNames));
for (Build dependencyBuild: dependencyBuilds) {
BuildDependence dependence = new BuildDependence();
dependence.setDependency(dependencyBuild);
dependence.setDependent(build);
build.getDependencies().add(dependence);
}
BuildDependence dependence = new BuildDependence();
dependence.setDependency(dependencyBuild);
dependence.setDependent(build);
build.getDependencies().add(dependence);
}
}.run();
@ -260,8 +233,10 @@ public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableT
buildManager.create(build);
listenerRegistry.post(new BuildSubmitted(build));
return build;
} else {
return builds.iterator().next();
}
return builds;
}
@Nullable
@ -396,8 +371,6 @@ public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableT
private void markBuildError(Build build, String errorMessage) {
build.setStatus(Build.Status.IN_ERROR, errorMessage);
build.setFinishDate(new Date());
if (build.isNew())
buildManager.create(build);
listenerRegistry.post(new BuildFinished(build));
}
@ -412,8 +385,16 @@ public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableT
if (ciSpec != null) {
for (Job job: ciSpec.getJobs()) {
JobTrigger trigger = job.getMatchedTrigger(event);
if (trigger != null)
submit(event.getProject(), commitId.name(), job.getName(), getParamMatrix(trigger.getParams()));
if (trigger != null) {
new MatrixRunner<List<String>>(JobParam.getParamMatrix(trigger.getParams())) {
@Override
public void run(Map<String, List<String>> paramMap) {
submit(event.getProject(), commitId.name(), job.getName(), paramMap);
}
}.run();
}
}
}
} catch (Exception e) {
@ -618,5 +599,5 @@ public class DefaultJobScheduler implements JobScheduler, Runnable, SchedulableT
public ScheduleBuilder<?> getScheduleBuilder() {
return CronScheduleBuilder.dailyAtHourAndMinute(0, 0);
}
}

View File

@ -3,7 +3,6 @@ package io.onedev.server.ci.job;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -15,6 +14,7 @@ import org.hibernate.validator.constraints.NotEmpty;
import io.onedev.server.ci.JobDependency;
import io.onedev.server.ci.job.cache.JobCache;
import io.onedev.server.ci.job.log.LogLevel;
import io.onedev.server.ci.job.param.JobParam;
import io.onedev.server.ci.job.trigger.JobTrigger;
import io.onedev.server.event.ProjectEvent;
import io.onedev.server.util.inputspec.InputSpec;
@ -229,11 +229,8 @@ public class Job implements Serializable, Validatable {
}
public Map<String, InputSpec> getParamSpecMap() {
if (paramSpecMap == null) {
paramSpecMap = new LinkedHashMap<>();
for (InputSpec paramSpec: getParamSpecs())
paramSpecMap.put(paramSpec.getName(), paramSpec);
}
if (paramSpecMap == null)
paramSpecMap = JobParam.getParamSpecMap(paramSpecs);
return paramSpecMap;
}

View File

@ -6,12 +6,12 @@ import java.util.Map;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;
public interface JobScheduler {
public interface JobManager {
void submit(Project project, String commitHash, String jobName, Map<String, List<List<String>>> paramMatrix);
Build submit(Project project, String commitHash, String jobName, Map<String, List<String>> paramMap);
void resubmit(Build build, Map<String, List<String>> paramMap);
void cancel(Build build);
}

View File

@ -5,6 +5,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -87,7 +88,7 @@ public class JobParam implements Serializable {
.toHashCode();
}
public static void validateValues(List<List<String>> values) {
public static void validateParamValues(List<List<String>> values) {
if (values.isEmpty())
throw new ValidationException("At least one value needs to be specified");
Set<List<String>> encountered = new HashSet<>();
@ -99,44 +100,64 @@ public class JobParam implements Serializable {
}
}
public static void validateParams(List<InputSpec> paramSpecs, Map<String, List<List<String>>> params) {
Map<String, InputSpec> paramSpecMap = new HashMap<>();
for (InputSpec paramSpec: paramSpecs) {
paramSpecMap.put(paramSpec.getName(), paramSpec);
if (!params.containsKey(paramSpec.getName()))
throw new ValidationException("Missing job parameter: " + paramSpec.getName());
}
for (Map.Entry<String, List<List<String>>> entry: params.entrySet()) {
InputSpec paramSpec = paramSpecMap.get(entry.getKey());
if (paramSpec == null)
throw new ValidationException("Unknown job parameter: " + entry.getKey());
public static void validateParamMatrix(Map<String, InputSpec> paramSpecMap, Map<String, List<List<String>>> paramMatrix) {
validateParamNames(paramSpecMap.keySet(), paramMatrix.keySet());
for (Map.Entry<String, List<List<String>>> entry: paramMatrix.entrySet()) {
InputSpec paramSpec = Preconditions.checkNotNull(paramSpecMap.get(entry.getKey()));
if (entry.getValue() != null) {
try {
validateValues(entry.getValue());
validateParamValues(entry.getValue());
} catch (ValidationException e) {
throw new ValidationException("Error validating values of parameter '"
+ entry.getKey() + "': " + e.getMessage());
}
for (List<String> value: entry.getValue()) {
try {
paramSpec.convertToObject(value);
} catch (Exception e) {
String displayValue;
if (paramSpec instanceof SecretInput)
displayValue = SecretInput.MASK;
else
displayValue = value.toString();
throw new ValidationException("Error validating value '" + displayValue + "' of parameter '"
+ entry.getKey() + "': " + e.getMessage());
}
}
for (List<String> value: entry.getValue())
validateParamValue(paramSpec, entry.getKey(), value);
}
}
}
private static void validateParamValue(InputSpec paramSpec, String paramName, List<String> paramValue) {
try {
paramSpec.convertToObject(paramValue);
} catch (Exception e) {
String displayValue;
if (paramSpec instanceof SecretInput)
displayValue = SecretInput.MASK;
else
displayValue = paramValue.toString();
throw new ValidationException("Error validating value '" + displayValue + "' of parameter '"
+ paramName + "': " + e.getMessage());
}
}
private static void validateParamNames(Collection<String> paramSpecNames, Collection<String> paramNames) {
for (String paramSpecName: paramSpecNames) {
if (!paramNames.contains(paramSpecName))
throw new ValidationException("Missing job parameter: " + paramSpecName);
}
for (String paramName: paramNames) {
if (!paramSpecNames.contains(paramName))
throw new ValidationException("Unknow job parameter: " + paramName);
}
}
public static void validateParamMap(Map<String, InputSpec> paramSpecMap, Map<String, List<String>> paramMap) {
validateParamNames(paramSpecMap.keySet(), paramMap.keySet());
for (Map.Entry<String, List<String>> entry: paramMap.entrySet()) {
InputSpec paramSpec = Preconditions.checkNotNull(paramSpecMap.get(entry.getKey()));
validateParamValue(paramSpec, entry.getKey(), entry.getValue());
}
}
public static Map<String, InputSpec> getParamSpecMap(List<InputSpec> paramSpecs) {
Map<String, InputSpec> paramSpecMap = new LinkedHashMap<>();
for (InputSpec paramSpec: paramSpecs)
paramSpecMap.put(paramSpec.getName(), paramSpec);
return paramSpecMap;
}
public static void validateParams(List<InputSpec> paramSpecs, List<JobParam> params) {
Map<String, List<List<String>>> paramMap = new HashMap<>();
for (JobParam param: params) {
@ -148,17 +169,7 @@ public class JobParam implements Serializable {
if (paramMap.put(param.getName(), values) != null)
throw new ValidationException("Duplicate param: " + param.getName());
}
validateParams(paramSpecs, paramMap);
}
public static Map<String, List<List<String>>> getParamMatrix(Map<String, List<String>> paramMap) {
Map<String, List<List<String>>> paramMatrix = new HashMap<>();
for (Map.Entry<String, List<String>> entry: paramMap.entrySet()) {
List<List<String>> values = new ArrayList<>();
values.add(entry.getValue());
paramMatrix.put(entry.getKey(), values);
}
return paramMatrix;
validateParamMatrix(getParamSpecMap(paramSpecs), paramMap);
}
@SuppressWarnings("unchecked")
@ -220,5 +231,12 @@ public class JobParam implements Serializable {
}
return paramMap;
}
public static Map<String, List<List<String>>> getParamMatrix(List<JobParam> params) {
Map<String, List<List<String>>> paramMatrix = new LinkedHashMap<>();
for (JobParam param: params)
paramMatrix.put(param.getName(), param.getValuesProvider().getValues());
return paramMatrix;
}
}

View File

@ -1,5 +1,6 @@
package io.onedev.server.entitymanager;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -17,13 +18,13 @@ public interface BuildManager extends EntityManager<Build> {
@Nullable
Build find(Project project, long number);
List<Build> query(Project project, String commitHash, @Nullable String jobName, Map<String, List<String>> params);
Collection<Build> query(Project project, String commitHash, @Nullable String jobName, Map<String, List<String>> params);
List<Build> query(Project project, String commitHash);
Collection<Build> query(Project project, String commitHash);
void create(Build build);
List<Build> queryUnfinished();
Collection<Build> queryUnfinished();
List<Build> query(Project project, String term, int count);

View File

@ -1,11 +0,0 @@
package io.onedev.server.entitymanager;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.BuildRequirement;
import io.onedev.server.persistence.dao.EntityManager;
public interface BuildRequirementManager extends EntityManager<BuildRequirement> {
void saveBuildRequirements(PullRequest request);
}

View File

@ -0,0 +1,11 @@
package io.onedev.server.entitymanager;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.PullRequestBuild;
import io.onedev.server.persistence.dao.EntityManager;
public interface PullRequestBuildManager extends EntityManager<PullRequestBuild> {
void savePullRequestBuilds(PullRequest request);
}

View File

@ -33,8 +33,6 @@ public interface PullRequestManager extends EntityManager<PullRequest> {
@Nullable
PullRequest findLatest(Project targetProject, User submitter);
Collection<PullRequest> queryOpenByCommit(String commitHash);
void discard(PullRequest request, @Nullable String note);
void reopen(PullRequest request, @Nullable String note);

View File

@ -1,6 +1,7 @@
package io.onedev.server.entitymanager.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -68,10 +69,6 @@ public class DefaultBuildManager extends AbstractEntityManager<Build> implements
@Transactional
@Override
public void delete(Build build) {
Query<?> query = getSession().createQuery("update BuildRequirement set build=null where build=:build");
query.setParameter("build", build);
query.executeUpdate();
FileUtils.deleteDir(storageManager.getBuildDir(build.getProject().getId(), build.getNumber()));
super.delete(build);
}
@ -88,13 +85,13 @@ public class DefaultBuildManager extends AbstractEntityManager<Build> implements
@Sessional
@Override
public List<Build> query(Project project, String commitHash) {
public Collection<Build> query(Project project, String commitHash) {
return query(project, commitHash, null, new HashMap<>());
}
@Sessional
@Override
public List<Build> query(Project project, String commitHash, String jobName, Map<String, List<String>> params) {
public Collection<Build> query(Project project, String commitHash, String jobName, Map<String, List<String>> params) {
CriteriaBuilder builder = getSession().getCriteriaBuilder();
CriteriaQuery<Build> query = builder.createQuery(Build.class);
Root<Build> root = query.from(Build.class);
@ -124,7 +121,7 @@ public class DefaultBuildManager extends AbstractEntityManager<Build> implements
@Sessional
@Override
public List<Build> queryUnfinished() {
public Collection<Build> queryUnfinished() {
EntityCriteria<Build> criteria = newCriteria();
criteria.add(Restrictions.or(
Restrictions.eq("status", Status.QUEUEING),

View File

@ -1,46 +0,0 @@
package io.onedev.server.entitymanager.impl;
import java.util.Collection;
import java.util.HashSet;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.persistence.Query;
import io.onedev.server.entitymanager.BuildRequirementManager;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.BuildRequirement;
import io.onedev.server.persistence.annotation.Transactional;
import io.onedev.server.persistence.dao.AbstractEntityManager;
import io.onedev.server.persistence.dao.Dao;
@Singleton
public class DefaultBuildRequirementManager extends AbstractEntityManager<BuildRequirement>
implements BuildRequirementManager {
@Inject
public DefaultBuildRequirementManager(Dao dao) {
super(dao);
}
@Transactional
@Override
public void saveBuildRequirements(PullRequest request) {
Collection<Long> ids = new HashSet<>();
for (BuildRequirement requirement: request.getBuildRequirements()) {
save(requirement);
ids.add(requirement.getId());
}
if (!ids.isEmpty()) {
Query query = getSession().createQuery("delete from BuildRequirement where request=:request and id not in (:ids)");
query.setParameter("request", request);
query.setParameter("ids", ids);
query.executeUpdate();
} else {
Query query = getSession().createQuery("delete from BuildRequirement where request=:request");
query.setParameter("request", request);
query.executeUpdate();
}
}
}

View File

@ -0,0 +1,54 @@
package io.onedev.server.entitymanager.impl;
import java.util.Collection;
import java.util.HashSet;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.persistence.Query;
import io.onedev.commons.launcher.loader.ListenerRegistry;
import io.onedev.server.entitymanager.PullRequestBuildManager;
import io.onedev.server.event.pullrequest.PullRequestBuildEvent;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.PullRequestBuild;
import io.onedev.server.persistence.annotation.Transactional;
import io.onedev.server.persistence.dao.AbstractEntityManager;
import io.onedev.server.persistence.dao.Dao;
@Singleton
public class DefaultPullRequestBuildManager extends AbstractEntityManager<PullRequestBuild>
implements PullRequestBuildManager {
private final ListenerRegistry listenerRegistry;
@Inject
public DefaultPullRequestBuildManager(Dao dao, ListenerRegistry listenerRegistry) {
super(dao);
this.listenerRegistry = listenerRegistry;
}
@Transactional
@Override
public void savePullRequestBuilds(PullRequest request) {
Collection<Long> ids = new HashSet<>();
for (PullRequestBuild pullRequestBuild: request.getPullRequestBuilds()) {
boolean isNew = pullRequestBuild.isNew();
save(pullRequestBuild);
if (isNew)
listenerRegistry.post(new PullRequestBuildEvent(pullRequestBuild));
ids.add(pullRequestBuild.getId());
}
if (!ids.isEmpty()) {
Query query = getSession().createQuery("delete from PullRequestBuild where request=:request and id not in (:ids)");
query.setParameter("request", request);
query.setParameter("ids", ids);
query.executeUpdate();
} else {
Query query = getSession().createQuery("delete from PullRequestBuild where request=:request");
query.setParameter("request", request);
query.executeUpdate();
}
}
}

View File

@ -17,7 +17,6 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -61,10 +60,9 @@ import io.onedev.server.OneDev;
import io.onedev.server.OneException;
import io.onedev.server.cache.CommitInfoManager;
import io.onedev.server.ci.JobDependency;
import io.onedev.server.ci.job.JobScheduler;
import io.onedev.server.ci.job.JobManager;
import io.onedev.server.ci.job.param.JobParam;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.BuildRequirementManager;
import io.onedev.server.entitymanager.PullRequestBuildManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.PullRequestChangeManager;
import io.onedev.server.entitymanager.PullRequestManager;
@ -83,7 +81,7 @@ import io.onedev.server.event.pullrequest.PullRequestOpened;
import io.onedev.server.git.GitUtils;
import io.onedev.server.git.command.FileChange;
import io.onedev.server.model.Build;
import io.onedev.server.model.BuildRequirement;
import io.onedev.server.model.PullRequestBuild;
import io.onedev.server.model.Group;
import io.onedev.server.model.Project;
import io.onedev.server.model.PullRequest;
@ -153,39 +151,36 @@ public class DefaultPullRequestManager extends AbstractEntityManager<PullRequest
private final PullRequestReviewManager pullRequestReviewManager;
private final BuildRequirementManager buildRequirementManager;
private final PullRequestBuildManager pullRequestBuildManager;
private final BatchWorkManager batchWorkManager;
private final PullRequestChangeManager pullRequestChangeManager;
private final BuildManager buildManager;
private final TransactionManager transactionManager;
private final JobScheduler jobScheduler;
private final JobManager jobManager;
@Inject
public DefaultPullRequestManager(Dao dao, PullRequestUpdateManager pullRequestUpdateManager,
PullRequestReviewManager pullRequestReviewManager, UserManager userManager,
MarkdownManager markdownManager, BatchWorkManager batchWorkManager,
ListenerRegistry listenerRegistry, SessionManager sessionManager,
PullRequestChangeManager pullRequestChangeManager, BuildManager buildManager,
BuildRequirementManager buildRequirementManager, TransactionManager transactionManager,
JobScheduler jobScheduler, ProjectManager projectManager) {
PullRequestChangeManager pullRequestChangeManager,
PullRequestBuildManager pullRequestBuildManager, TransactionManager transactionManager,
JobManager jobManager, ProjectManager projectManager) {
super(dao);
this.pullRequestUpdateManager = pullRequestUpdateManager;
this.pullRequestReviewManager = pullRequestReviewManager;
this.transactionManager = transactionManager;
this.buildManager = buildManager;
this.userManager = userManager;
this.batchWorkManager = batchWorkManager;
this.sessionManager = sessionManager;
this.listenerRegistry = listenerRegistry;
this.pullRequestChangeManager = pullRequestChangeManager;
this.buildRequirementManager = buildRequirementManager;
this.jobScheduler = jobScheduler;
this.pullRequestBuildManager = pullRequestBuildManager;
this.jobManager = jobManager;
this.projectManager = projectManager;
}
@ -376,7 +371,7 @@ public class DefaultPullRequestManager extends AbstractEntityManager<PullRequest
pullRequestUpdateManager.save(update, false);
pullRequestReviewManager.saveReviews(request);
buildRequirementManager.saveBuildRequirements(request);
pullRequestBuildManager.savePullRequestBuilds(request);
checkAsync(request);
@ -445,12 +440,12 @@ public class DefaultPullRequestManager extends AbstractEntityManager<PullRequest
checkQuality(request);
pullRequestReviewManager.saveReviews(request);
buildRequirementManager.saveBuildRequirements(request);
pullRequestBuildManager.savePullRequestBuilds(request);
MergePreview mergePreview = request.getMergePreview();
MergePreview preview = request.getMergePreview();
if (request.isAllReviewsApproved() && request.isAllBuildsSuccessful()
&& mergePreview != null && mergePreview.getMerged() != null) {
&& preview != null && preview.getMerged() != null) {
merge(request);
}
}
@ -553,7 +548,13 @@ public class DefaultPullRequestManager extends AbstractEntityManager<PullRequest
ofOpen(),
Restrictions.or(ofSource(projectAndBranch), ofTarget(projectAndBranch)));
for (PullRequest request: query(EntityCriteria.of(PullRequest.class).add(criterion))) {
check(request);
try {
check(request);
} catch (Exception e) {
String message = String.format("Error checking pull request (project: %s, number: %d)",
request.getTargetProject().getName(), request.getNumber());
logger.error(message, e);
}
}
}
}
@ -637,9 +638,9 @@ public class DefaultPullRequestManager extends AbstractEntityManager<PullRequest
@Transactional
public void on(BuildEvent event) {
Build build = event.getBuild();
for (PullRequest request: queryOpenByCommit(build.getCommitHash())) {
listenerRegistry.post(new PullRequestBuildEvent(request, build));
checkAsync(request);
for (PullRequestBuild pullRequestBuild: build.getPullRequestBuilds()) {
listenerRegistry.post(new PullRequestBuildEvent(pullRequestBuild));
checkAsync(pullRequestBuild.getRequest());
}
}
@ -664,7 +665,9 @@ public class DefaultPullRequestManager extends AbstractEntityManager<PullRequest
ThreadContext.bind(subject);
check(load(requestId));
} catch (Exception e) {
logger.error("Error checking pull request status", e);
String message = String.format("Error checking pull request (project: %s, number: %d)",
request.getTargetProject().getName(), request.getNumber());
logger.error(message, e);
} finally {
ThreadContext.unbindSubject();
}
@ -711,68 +714,35 @@ public class DefaultPullRequestManager extends AbstractEntityManager<PullRequest
}
private void checkBuilds(PullRequest request, List<JobDependency> jobDependencies) {
String commit;
Collection<PullRequestBuild> prevRequirements = new ArrayList<>(request.getPullRequestBuilds());
request.getPullRequestBuilds().clear();
MergePreview preview = request.getMergePreview();
if (preview != null && preview.getMerged() != null)
commit = preview.getMerged();
else
commit = null;
List<Build> builds;
if (commit != null)
builds = buildManager.query(request.getTargetProject(), commit);
else
builds = new ArrayList<>();
Collection<BuildRequirement> effectiveRequirements = new HashSet<>();
for (JobDependency dependency: jobDependencies) {
Map<String, List<List<String>>> paramMatrix = new HashMap<>();
Set<String> secretParamNames = new HashSet<>();
for (JobParam param: dependency.getJobParams()) {
paramMatrix.put(param.getName(), param.getValuesProvider().getValues());
if (param.isSecret())
secretParamNames.add(param.getName());
}
new MatrixRunner<List<String>>(paramMatrix) {
@Override
public void run(Map<String, List<String>> params) {
BuildRequirement requirement = request.getBuildRequirement(dependency.getJobName(), params);
if (requirement == null) {
requirement = new BuildRequirement();
requirement.setJobName(dependency.getJobName());
requirement.setBuildParams(new LinkedHashMap<>(params));
requirement.setRequest(request);
request.getBuildRequirements().add(requirement);
}
effectiveRequirements.add(requirement);
if (preview != null && preview.getMerged() != null) {
for (JobDependency dependency: jobDependencies) {
new MatrixRunner<List<String>>(JobParam.getParamMatrix(dependency.getJobParams())) {
Build build = null;
for (Build each: builds) {
Map<String, List<String>> paramsWithoutSecrets = new HashMap<>(params);
Map<String, List<String>> buildParamsWithoutSecrets = new HashMap<>(each.getParamMap());
paramsWithoutSecrets.keySet().removeAll(secretParamNames);
buildParamsWithoutSecrets.keySet().removeAll(secretParamNames);
if (each.getJobName().equals(dependency.getJobName()) && buildParamsWithoutSecrets.equals(paramsWithoutSecrets)) {
build = each;
break;
@Override
public void run(Map<String, List<String>> paramMap) {
Build build = jobManager.submit(request.getTargetProject(), preview.getMerged(),
dependency.getJobName(), paramMap);
PullRequestBuild pullRequestBuild = null;
for (PullRequestBuild prevRequirement: prevRequirements) {
if (prevRequirement.getBuild().equals(build)) {
pullRequestBuild = prevRequirement;
break;
}
}
if (pullRequestBuild == null) {
pullRequestBuild = new PullRequestBuild();
pullRequestBuild.setRequest(request);
pullRequestBuild.setBuild(build);
}
request.getPullRequestBuilds().add(pullRequestBuild);
}
requirement.setBuild(build);
if (commit != null) {
jobScheduler.submit(request.getTargetProject(), commit,
dependency.getJobName(), JobParam.getParamMatrix(params));
}
}
}.run();
}.run();
}
}
request.getBuildRequirements().retainAll(effectiveRequirements);
}
private void checkReviews(ReviewRequirement reviewRequirement, PullRequestUpdate update) {
@ -948,18 +918,6 @@ public class DefaultPullRequestManager extends AbstractEntityManager<PullRequest
return addedContributions;
}
@Transactional
@Override
public Collection<PullRequest> queryOpenByCommit(String commitHash) {
EntityCriteria<PullRequest> criteria = EntityCriteria.of(PullRequest.class);
criteria.add(PullRequest.CriterionHelper.ofOpen());
Criterion verifyCommitCriterion = Restrictions.or(
Restrictions.eq("headCommitHash", commitHash),
Restrictions.eq("lastMergePreview.merged", commitHash));
criteria.add(verifyCommitCriterion);
return query(criteria);
}
private Predicate[] getPredicates(io.onedev.server.search.entity.EntityCriteria<PullRequest> criteria,
Project targetProject, User user, Root<PullRequest> root, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<>();

View File

@ -2,20 +2,19 @@ package io.onedev.server.event.pullrequest;
import java.util.Date;
import io.onedev.server.model.Build;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.PullRequestBuild;
public class PullRequestBuildEvent extends PullRequestEvent {
private final Build build;
private final PullRequestBuild pullRequestBuild;
public PullRequestBuildEvent(PullRequest request, Build build) {
super(null, new Date(), request);
this.build = build;
public PullRequestBuildEvent(PullRequestBuild pullRequestBuild) {
super(null, new Date(), pullRequestBuild.getRequest());
this.pullRequestBuild = pullRequestBuild;
}
public Build getBuild() {
return build;
public PullRequestBuild getPullRequestBuild() {
return pullRequestBuild;
}
}

View File

@ -18,6 +18,7 @@ import io.onedev.commons.launcher.bootstrap.Bootstrap;
import io.onedev.server.persistence.DefaultPersistManager;
import io.onedev.server.persistence.HibernateProperties;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.dao.Dao;
import io.onedev.server.util.validation.EntityValidator;
@ -31,8 +32,9 @@ public class ApplyDatabaseConstraints extends DefaultPersistManager {
@Inject
public ApplyDatabaseConstraints(PhysicalNamingStrategy physicalNamingStrategy,
HibernateProperties properties, Interceptor interceptor,
IdManager idManager, Dao dao, EntityValidator validator) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator);
IdManager idManager, Dao dao, EntityValidator validator,
TransactionManager transactionManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator, transactionManager);
}
@Override

View File

@ -18,6 +18,7 @@ import io.onedev.commons.utils.ZipUtils;
import io.onedev.server.persistence.DefaultPersistManager;
import io.onedev.server.persistence.HibernateProperties;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.dao.Dao;
import io.onedev.server.util.validation.EntityValidator;
@ -31,8 +32,9 @@ public class BackupDatabase extends DefaultPersistManager {
@Inject
public BackupDatabase(PhysicalNamingStrategy physicalNamingStrategy,
HibernateProperties properties, Interceptor interceptor,
IdManager idManager, Dao dao, EntityValidator validator) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator);
IdManager idManager, Dao dao, EntityValidator validator,
TransactionManager transactionManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator, transactionManager);
}
@Override

View File

@ -13,6 +13,7 @@ import io.onedev.commons.launcher.bootstrap.Bootstrap;
import io.onedev.server.persistence.DefaultPersistManager;
import io.onedev.server.persistence.HibernateProperties;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.dao.Dao;
import io.onedev.server.util.validation.EntityValidator;
@ -26,8 +27,9 @@ public class CheckDataVersion extends DefaultPersistManager {
@Inject
public CheckDataVersion(PhysicalNamingStrategy physicalNamingStrategy,
HibernateProperties properties, Interceptor interceptor,
IdManager idManager, Dao dao, EntityValidator validator) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator);
IdManager idManager, Dao dao, EntityValidator validator,
TransactionManager transactionManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator, transactionManager);
}
@Override

View File

@ -14,6 +14,7 @@ import io.onedev.commons.launcher.bootstrap.Bootstrap;
import io.onedev.server.persistence.DefaultPersistManager;
import io.onedev.server.persistence.HibernateProperties;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.dao.Dao;
import io.onedev.server.util.validation.EntityValidator;
@ -27,8 +28,9 @@ public class CleanDatabase extends DefaultPersistManager {
@Inject
public CleanDatabase(PhysicalNamingStrategy physicalNamingStrategy,
HibernateProperties properties, Interceptor interceptor,
IdManager idManager, Dao dao, EntityValidator validator) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator);
IdManager idManager, Dao dao, EntityValidator validator,
TransactionManager transactionManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator, transactionManager);
}
@Override

View File

@ -10,6 +10,7 @@ import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import io.onedev.server.persistence.DefaultPersistManager;
import io.onedev.server.persistence.HibernateProperties;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.dao.Dao;
import io.onedev.server.util.validation.EntityValidator;
@ -21,8 +22,9 @@ public class DatabaseDialect extends DefaultPersistManager {
@Inject
public DatabaseDialect(PhysicalNamingStrategy physicalNamingStrategy,
HibernateProperties properties, Interceptor interceptor,
IdManager idManager, Dao dao, EntityValidator validator) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator);
IdManager idManager, Dao dao, EntityValidator validator,
TransactionManager transactionManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator, transactionManager);
}
@Override

View File

@ -16,6 +16,7 @@ import io.onedev.server.model.User;
import io.onedev.server.persistence.DefaultPersistManager;
import io.onedev.server.persistence.HibernateProperties;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.dao.Dao;
import io.onedev.server.util.validation.EntityValidator;
@ -33,8 +34,9 @@ public class ResetAdminPassword extends DefaultPersistManager {
@Inject
public ResetAdminPassword(PhysicalNamingStrategy physicalNamingStrategy, HibernateProperties properties,
Interceptor interceptor, IdManager idManager, Dao dao,
EntityValidator validator, UserManager userManager, PasswordService passwordService) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator);
EntityValidator validator, UserManager userManager, PasswordService passwordService,
TransactionManager transactionManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator, transactionManager);
this.userManager = userManager;
this.passwordService = passwordService;
}

View File

@ -18,6 +18,7 @@ import io.onedev.commons.utils.ZipUtils;
import io.onedev.server.persistence.DefaultPersistManager;
import io.onedev.server.persistence.HibernateProperties;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.dao.Dao;
import io.onedev.server.util.validation.EntityValidator;
@ -31,8 +32,9 @@ public class RestoreDatabase extends DefaultPersistManager {
@Inject
public RestoreDatabase(PhysicalNamingStrategy physicalNamingStrategy,
HibernateProperties properties, Interceptor interceptor,
IdManager idManager, Dao dao, EntityValidator validator) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator);
IdManager idManager, Dao dao, EntityValidator validator,
TransactionManager transactionManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator, transactionManager);
}
@Override

View File

@ -32,6 +32,7 @@ import io.onedev.server.migration.MigrationHelper;
import io.onedev.server.persistence.DefaultPersistManager;
import io.onedev.server.persistence.HibernateProperties;
import io.onedev.server.persistence.IdManager;
import io.onedev.server.persistence.TransactionManager;
import io.onedev.server.persistence.dao.Dao;
import io.onedev.server.util.validation.EntityValidator;
@ -49,8 +50,9 @@ public class Upgrade extends DefaultPersistManager {
@Inject
public Upgrade(PhysicalNamingStrategy physicalNamingStrategy,
HibernateProperties properties, Interceptor interceptor,
IdManager idManager, Dao dao, EntityValidator validator, PluginManager pluginManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator);
IdManager idManager, Dao dao, EntityValidator validator, PluginManager pluginManager,
TransactionManager transactionManager) {
super(physicalNamingStrategy, properties, interceptor, idManager, dao, validator, transactionManager);
appName = pluginManager.getProduct().getName();
}

View File

@ -136,6 +136,9 @@ public class Build extends AbstractEntity implements Referenceable {
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
private Collection<BuildDependence> dependents= new ArrayList<>();
@OneToMany(mappedBy="build", cascade=CascadeType.REMOVE)
private Collection<PullRequestBuild> pullRequestBuilds = new ArrayList<>();
private transient Map<String, List<String>> paramMap;
private transient Optional<Collection<Long>> fixedIssueNumbers;
@ -309,6 +312,14 @@ public class Build extends AbstractEntity implements Referenceable {
this.statusMessage = statusMessage;
}
public Collection<PullRequestBuild> getPullRequestBuilds() {
return pullRequestBuilds;
}
public void setPullRequestBuilds(Collection<PullRequestBuild> pullRequestBuilds) {
this.pullRequestBuilds = pullRequestBuilds;
}
public Map<String, List<String>> getParamMap() {
if (paramMap == null) {
paramMap = new HashMap<>();

View File

@ -10,6 +10,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -435,7 +436,17 @@ public class Project extends AbstractEntity implements Validatable {
}
public List<RefInfo> getBranches() {
return getRefInfos(Constants.R_HEADS);
List<RefInfo> refInfos = getRefInfos(Constants.R_HEADS);
for (Iterator<RefInfo> it = refInfos.iterator(); it.hasNext();) {
RefInfo refInfo = it.next();
if (refInfo.getRef().getName().equals(GitUtils.branch2ref(getDefaultBranch()))) {
it.remove();
refInfos.add(0, refInfo);
break;
}
}
return refInfos;
}
public List<RefInfo> getTags() {

View File

@ -8,7 +8,6 @@ import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@ -156,7 +155,7 @@ public class PullRequest extends AbstractEntity implements Referenceable, Attach
private Collection<PullRequestReview> reviews = new ArrayList<>();
@OneToMany(mappedBy="request", cascade=CascadeType.REMOVE)
private Collection<BuildRequirement> buildRequirements = new ArrayList<>();
private Collection<PullRequestBuild> pullRequestBuilds = new ArrayList<>();
@OneToMany(mappedBy="request", cascade=CascadeType.REMOVE)
private Collection<PullRequestComment> comments = new ArrayList<>();
@ -343,12 +342,12 @@ public class PullRequest extends AbstractEntity implements Referenceable, Attach
sortedUpdates = null;
}
public Collection<BuildRequirement> getBuildRequirements() {
return buildRequirements;
public Collection<PullRequestBuild> getPullRequestBuilds() {
return pullRequestBuilds;
}
public void setBuildRequirements(Collection<BuildRequirement> buildRequirements) {
this.buildRequirements = buildRequirements;
public void setPullRequestBuilds(Collection<PullRequestBuild> pullRequestBuilds) {
this.pullRequestBuilds = pullRequestBuilds;
}
public Collection<PullRequestComment> getComments() {
@ -711,15 +710,6 @@ public class PullRequest extends AbstractEntity implements Referenceable, Attach
return null;
}
@Nullable
public BuildRequirement getBuildRequirement(String jobName, Map<String, List<String>> buildParams) {
for (BuildRequirement requirement: getBuildRequirements()) {
if (requirement.getJobName().equals(jobName) && requirement.getBuildParams().equals(buildParams))
return requirement;
}
return null;
}
@Nullable
public PullRequestReview getReview(User user) {
for (PullRequestReview review: getReviews()) {
@ -819,8 +809,8 @@ public class PullRequest extends AbstractEntity implements Referenceable, Attach
}
public boolean isAllBuildsSuccessful() {
for (BuildRequirement requirement: getBuildRequirements()) {
if (requirement.getBuild() == null || requirement.getBuild().getStatus() != Build.Status.SUCCESSFUL)
for (PullRequestBuild requirement: getPullRequestBuilds()) {
if (requirement.getBuild().getStatus() != Build.Status.SUCCESSFUL)
return false;
}
return true;

View File

@ -1,55 +1,28 @@
package io.onedev.server.model;
import java.util.LinkedHashMap;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(indexes={@Index(columnList="o_request_id")})
public class BuildRequirement extends AbstractEntity {
@Table(indexes={@Index(columnList="o_request_id"), @Index(columnList="o_build_id")})
public class PullRequestBuild extends AbstractEntity {
private static final long serialVersionUID = 1L;
public static final String ATTR_BUILD = "build";
@Column(nullable=false)
private String jobName;
@Column(nullable=false, length=65535)
@Lob
private LinkedHashMap<String, List<String>> buildParams = new LinkedHashMap<>();
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(nullable=false)
private PullRequest request;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(nullable=false)
private Build build;
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public LinkedHashMap<String, List<String>> getBuildParams() {
return buildParams;
}
public void setBuildParams(LinkedHashMap<String, List<String>> buildParams) {
this.buildParams = buildParams;
}
public Build getBuild() {
return build;
}

View File

@ -2,6 +2,7 @@ package io.onedev.server.model.support;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -258,7 +259,7 @@ public class BranchProtection implements Serializable {
if (!ReviewRequirement.fromString(getReviewRequirement()).satisfied(user))
return false;
List<Build> builds = OneDev.getInstance(BuildManager.class).query(project, newObjectId.name());
Collection<Build> builds = OneDev.getInstance(BuildManager.class).query(project, newObjectId.name());
for (JobDependency dependency: getJobDependencies()) {
Map<String, List<List<String>>> paramMatrix = new HashMap<>();

View File

@ -24,6 +24,12 @@ public class KubernetesExecutor extends JobExecutor {
logger.info("run_type: " + envVars.get("run_type"));
logger.info("deploy_to_production_environment: " + envVars.get("deploy_to_production_environment"));
logger.info("production_token: " + envVars.get("production_token"));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override

View File

@ -169,7 +169,7 @@ public class PullRequestNotificationManager implements PersistListener {
mailManager.sendMailAsync(Lists.newArrayList(request.getSubmitter().getEmail()), subject, body);
notifiedUsers.add(request.getSubmitter());
} else if (event instanceof PullRequestBuildEvent) {
Build build = ((PullRequestBuildEvent) event).getBuild();
Build build = ((PullRequestBuildEvent) event).getPullRequestBuild().getBuild();
if (build.getStatus() == Build.Status.IN_ERROR
|| build.getStatus() == Build.Status.FAILED
|| build.getStatus() == Build.Status.CANCELLED

View File

@ -35,7 +35,7 @@ public class DefaultIdManager implements IdManager {
CriteriaQuery<Number> query = builder.createQuery(Number.class);
Root<?> root = query.from(entityClass);
query.select(builder.max(root.get("id")));
Number result = dao.getSessionManager().getSession().createQuery(query).getSingleResult();
Number result = dao.getSession().createQuery(query).getSingleResult();
return result!=null?result.longValue():0;
}

View File

@ -81,18 +81,22 @@ public class DefaultPersistManager implements PersistManager {
protected final StandardServiceRegistry serviceRegistry;
protected final TransactionManager transactionManager;
protected volatile SessionFactory sessionFactory;
@Inject
public DefaultPersistManager(PhysicalNamingStrategy physicalNamingStrategy,
HibernateProperties properties, Interceptor interceptor,
IdManager idManager, Dao dao, EntityValidator validator) {
IdManager idManager, Dao dao, EntityValidator validator,
TransactionManager transactionManager) {
this.physicalNamingStrategy = physicalNamingStrategy;
this.properties = properties;
this.interceptor = interceptor;
this.idManager = idManager;
this.dao = dao;
this.validator = validator;
this.transactionManager = transactionManager;
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(properties).build();
}
@ -178,19 +182,16 @@ public class DefaultPersistManager implements PersistManager {
idManager.init();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
try {
ModelVersion dataVersion = new ModelVersion();
dataVersion.versionColumn = MigrationHelper.getVersion(DatabaseMigrator.class);
session.save(dataVersion);
session.flush();
transaction.commit();
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
transactionManager.run(new Runnable() {
@Override
public void run() {
ModelVersion dataVersion = new ModelVersion();
dataVersion.versionColumn = MigrationHelper.getVersion(DatabaseMigrator.class);
transactionManager.getSession().save(dataVersion);
}
});
} else {
sessionFactory = metadata.getSessionFactoryBuilder().applyInterceptor(interceptor).build();
idManager.init();
@ -437,7 +438,7 @@ public class DefaultPersistManager implements PersistManager {
@Sessional
@Override
public void importData(Metadata metadata, File dataDir) {
Session session = dao.getSessionManager().getSession();
Session session = dao.getSession();
List<Class<?>> entityTypes = getEntityTypes(sessionFactory);
Collections.reverse(entityTypes);
for (Class<?> entityType: entityTypes) {

View File

@ -3,6 +3,8 @@ package io.onedev.server.persistence;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import javax.inject.Provider;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.slf4j.Logger;
@ -19,7 +21,7 @@ public class DefaultSessionManager implements SessionManager {
private static final Logger logger = LoggerFactory.getLogger(DefaultSessionManager.class);
private final PersistManager persistManager;
private final Provider<PersistManager> persistManagerProvider;
private final ExecutorService executorService;
@ -31,7 +33,7 @@ public class DefaultSessionManager implements SessionManager {
@Override
protected Session openObject() {
Session session = persistManager.getSessionFactory().openSession();
Session session = persistManagerProvider.get().getSessionFactory().openSession();
// Session is supposed to be able to write only in transactional methods
session.setHibernateFlushMode(FlushMode.MANUAL);
@ -49,8 +51,8 @@ public class DefaultSessionManager implements SessionManager {
};
@Inject
public DefaultSessionManager(PersistManager persistManager, ExecutorService executorService) {
this.persistManager = persistManager;
public DefaultSessionManager(Provider<PersistManager> persistManagerProvider, ExecutorService executorService) {
this.persistManagerProvider = persistManagerProvider;
this.executorService = executorService;
}
@ -71,7 +73,7 @@ public class DefaultSessionManager implements SessionManager {
@Override
public <T> T call(Callable<T> callable) {
if (persistManager.getSessionFactory() != null) {
if (persistManagerProvider.get().getSessionFactory() != null) {
openSession();
try {
return callable.call();

View File

@ -155,10 +155,20 @@ public class DefaultTransactionManager implements TransactionManager {
});
}
@Override
public SessionManager getSessionManager() {
return sessionManager;
}
@Override
public Transaction getTransaction() {
return sessionManager.getSession().getTransaction();
return getSession().getTransaction();
}
@Override
public Session getSession() {
return sessionManager.getSession();
}
}

View File

@ -18,12 +18,17 @@ package io.onedev.server.persistence;
import java.util.concurrent.Callable;
import org.hibernate.Session;
import org.hibernate.Transaction;
public interface TransactionManager {
Transaction getTransaction();
SessionManager getSessionManager();
Session getSession();
<T> T call(Callable<T> callable);
void run(Runnable runnable);

View File

@ -112,7 +112,7 @@ public abstract class AbstractEntityManager<T extends AbstractEntity> implements
}
protected Session getSession() {
return dao.getSessionManager().getSession();
return dao.getSession();
}
}

View File

@ -3,6 +3,7 @@ package io.onedev.server.persistence.dao;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import io.onedev.server.model.AbstractEntity;
@ -96,4 +97,5 @@ public interface Dao {
SessionManager getSessionManager();
Session getSession();
}

View File

@ -8,6 +8,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import io.onedev.commons.launcher.loader.ListenerRegistry;
@ -37,34 +38,34 @@ public class DefaultDao implements Dao, Serializable {
@Sessional
@Override
public <T extends AbstractEntity> T get(Class<T> entityClass, Long entityId) {
return (T) sessionManager.getSession().get(ClassUtils.unproxy(entityClass), entityId);
return (T) getSession().get(ClassUtils.unproxy(entityClass), entityId);
}
@Sessional
@Override
public <T extends AbstractEntity> T load(Class<T> entityClass, Long entityId) {
return (T) sessionManager.getSession().load(ClassUtils.unproxy(entityClass), entityId);
return (T) getSession().load(ClassUtils.unproxy(entityClass), entityId);
}
@Transactional
@Override
public void persist(AbstractEntity entity) {
boolean isNew = entity.isNew();
sessionManager.getSession().saveOrUpdate(entity);
getSession().saveOrUpdate(entity);
listenerRegistry.post(new EntityPersisted(entity, isNew));
}
@Transactional
@Override
public void remove(AbstractEntity entity) {
sessionManager.getSession().delete(entity);
getSession().delete(entity);
listenerRegistry.post(new EntityRemoved(entity));
}
@Sessional
@Override
public <T extends AbstractEntity> List<T> query(EntityCriteria<T> entityCriteria, int firstResult, int maxResults) {
Criteria criteria = entityCriteria.getExecutableCriteria(sessionManager.getSession());
Criteria criteria = entityCriteria.getExecutableCriteria(getSession());
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
return criteria.list();
@ -79,7 +80,7 @@ public class DefaultDao implements Dao, Serializable {
@Transactional
@Override
public <T extends AbstractEntity> T find(EntityCriteria<T> entityCriteria) {
Criteria criteria = entityCriteria.getExecutableCriteria(sessionManager.getSession());
Criteria criteria = entityCriteria.getExecutableCriteria(getSession());
criteria.setFirstResult(0);
criteria.setMaxResults(1);
return (T) criteria.uniqueResult();
@ -87,7 +88,7 @@ public class DefaultDao implements Dao, Serializable {
@Override
public <T extends AbstractEntity> int count(EntityCriteria<T> entityCriteria) {
Criteria criteria = entityCriteria.getExecutableCriteria(sessionManager.getSession());
Criteria criteria = entityCriteria.getExecutableCriteria(getSession());
criteria.setProjection(Projections.rowCount());
return ((Long) criteria.uniqueResult()).intValue();
}
@ -107,4 +108,9 @@ public class DefaultDao implements Dao, Serializable {
return sessionManager;
}
@Override
public Session getSession() {
return sessionManager.getSession();
}
}

View File

@ -8,7 +8,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import io.onedev.server.model.Build;
import io.onedev.server.model.BuildRequirement;
import io.onedev.server.model.PullRequestBuild;
import io.onedev.server.model.Project;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.User;
@ -22,7 +22,7 @@ public class HasFailedBuildsCriteria extends PullRequestCriteria {
public Predicate getPredicate(Project project, Root<PullRequest> root, CriteriaBuilder builder, User user) {
From<?, ?> join = root
.join(PullRequestConstants.ATTR_BUILDS, JoinType.LEFT)
.join(BuildRequirement.ATTR_BUILD, JoinType.LEFT);
.join(PullRequestBuild.ATTR_BUILD, JoinType.LEFT);
Path<?> status = join.get(Build.STATUS);
return builder.or(
@ -34,7 +34,7 @@ public class HasFailedBuildsCriteria extends PullRequestCriteria {
@Override
public boolean matches(PullRequest request, User user) {
for (BuildRequirement build: request.getBuildRequirements()) {
for (PullRequestBuild build: request.getPullRequestBuilds()) {
if (build.getBuild() != null &&
(build.getBuild().getStatus() == Build.Status.IN_ERROR
|| build.getBuild().getStatus() == Build.Status.FAILED

View File

@ -8,7 +8,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import io.onedev.server.model.Build;
import io.onedev.server.model.BuildRequirement;
import io.onedev.server.model.PullRequestBuild;
import io.onedev.server.model.Project;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.User;
@ -22,7 +22,7 @@ public class HasPendingBuildsCriteria extends PullRequestCriteria {
public Predicate getPredicate(Project project, Root<PullRequest> root, CriteriaBuilder builder, User user) {
From<?, ?> join = root
.join(PullRequestConstants.ATTR_BUILDS, JoinType.LEFT)
.join(BuildRequirement.ATTR_BUILD, JoinType.LEFT);
.join(PullRequestBuild.ATTR_BUILD, JoinType.LEFT);
Path<?> status = join.get(Build.STATUS);
@ -35,7 +35,7 @@ public class HasPendingBuildsCriteria extends PullRequestCriteria {
@Override
public boolean matches(PullRequest request, User user) {
for (BuildRequirement build: request.getBuildRequirements()) {
for (PullRequestBuild build: request.getPullRequestBuilds()) {
if (build.getBuild() == null
|| build.getBuild().getStatus() == Build.Status.RUNNING
|| build.getBuild().getStatus() == Build.Status.QUEUEING

View File

@ -6,7 +6,7 @@ import java.util.Map;
public abstract class MatrixRunner<T> {
private final Map<String, T> params;
private final Map<String, T> paramMap;
private final Map<String, List<T>> paramMatrix;
@ -14,8 +14,8 @@ public abstract class MatrixRunner<T> {
this(new LinkedHashMap<>(), paramMatrix);
}
private MatrixRunner(Map<String, T> params, Map<String, List<T>> paramMatrix) {
this.params = params;
private MatrixRunner(Map<String, T> paramMap, Map<String, List<T>> paramMatrix) {
this.paramMap = paramMap;
this.paramMatrix = paramMatrix;
}
@ -23,7 +23,7 @@ public abstract class MatrixRunner<T> {
if (!paramMatrix.isEmpty()) {
Map.Entry<String, List<T>> entry = paramMatrix.entrySet().iterator().next();
for (T value: entry.getValue()) {
Map<String, T> paramsCopy = new LinkedHashMap<>(params);
Map<String, T> paramsCopy = new LinkedHashMap<>(paramMap);
paramsCopy.put(entry.getKey(), value);
Map<String, List<T>> matrixCopy = new LinkedHashMap<>(paramMatrix);
matrixCopy.remove(entry.getKey());
@ -37,7 +37,7 @@ public abstract class MatrixRunner<T> {
}.run();
}
} else {
run(params);
run(paramMap);
}
}

View File

@ -33,7 +33,7 @@ onedev.server.codemirror = {
onedev.server.codemirror.setMode(cm, modeInfo);
},
setModeByFileName: function(cm, fileName) {
var modeInfo = CodeMirror.findModeByFileName(filePath);
var modeInfo = CodeMirror.findModeByFileName(fileName);
if (modeInfo)
onedev.server.codemirror.setMode(cm, modeInfo);
},

View File

@ -7,6 +7,7 @@
-ms-overflow-style: none;
touch-action: auto;
-ms-touch-action: auto;
position: relative;
}
/*

View File

@ -39,6 +39,7 @@ import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -329,10 +330,11 @@ public abstract class BuildListPanel extends Panel {
@Override
public void populateItem(Item<ICellPopulator<Build>> cellItem, String componentId, IModel<Build> rowModel) {
Fragment fragment = new Fragment(componentId, "statusFrag", BuildListPanel.this);
fragment.add(new BuildStatusIcon("icon", rowModel));
Long buildId = rowModel.getObject().getId();
Fragment fragment = new Fragment(componentId, "statusFrag", BuildListPanel.this);
fragment.add(new BuildStatusIcon("icon", buildId, true));
fragment.add(new Label("label", new AbstractReadOnlyModel<String>() {
@Override
@ -373,7 +375,7 @@ public abstract class BuildListPanel extends Panel {
return getProject();
}
}, build.getCommitHash(), build.getJobName());
}, ObjectId.fromString(build.getCommitHash()), build.getJobName());
link.add(new Label("label", build.getJobName()));
fragment.add(link);
cellItem.add(fragment);

View File

@ -23,6 +23,7 @@ import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.time.Duration;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import io.onedev.server.OneDev;
@ -118,7 +119,7 @@ public abstract class BuildSidePanel extends Panel {
return getProject();
}
}, getBuild().getCommitHash(), getBuild().getJobName());
}, ObjectId.fromString(getBuild().getCommitHash()), getBuild().getJobName());
jobLink.add(new Label("label", getBuild().getJobName()));
general.add(jobLink);

View File

@ -1,12 +0,0 @@
<wicket:panel>
<table class="build-status-list">
<tr wicket:id="requirements">
<td class="status">
<span wicket:id="icon"></span> <span wicket:id="name"></span>
</td>
<td class="title">
<a wicket:id="title"><span wicket:id="label"></span></a>
</td>
</tr>
</table>
</wicket:panel>

View File

@ -1,144 +0,0 @@
package io.onedev.server.web.component.build.status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import io.onedev.server.model.Build;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.BuildRequirement;
import io.onedev.server.web.page.project.builds.detail.BuildLogPage;
import io.onedev.server.web.websocket.PageDataChanged;
@SuppressWarnings("serial")
public class BuildRequirementListPanel extends GenericPanel<PullRequest> {
public BuildRequirementListPanel(String id, IModel<PullRequest> model) {
super(id, model);
}
private PullRequest getPullRequest() {
return getModelObject();
}
@Override
public void onEvent(IEvent<?> event) {
super.onEvent(event);
if (event.getPayload() instanceof PageDataChanged && isVisibleInHierarchy()) {
PageDataChanged pageDataChanged = (PageDataChanged) event.getPayload();
pageDataChanged.getHandler().add(this);
}
}
@Override
protected void onInitialize() {
super.onInitialize();
add(new ListView<BuildRequirement>("requirements", new LoadableDetachableModel<List<BuildRequirement>>() {
@Override
protected List<BuildRequirement> load() {
List<BuildRequirement> requirements = new ArrayList<>(getPullRequest().getBuildRequirements());
List<String> jobNames = getPullRequest().getTargetProject().getJobNames();
Collections.sort(requirements, new Comparator<BuildRequirement>() {
@Override
public int compare(BuildRequirement o1, BuildRequirement o2) {
int index1 = jobNames.indexOf(o1.getJobName());
int index2 = jobNames.indexOf(o2.getJobName());
if (index1 != index2)
return index1 - index2;
else if (!o1.getJobName().equals(o2.getJobName()))
return o1.getJobName().compareTo(o2.getJobName());
else if (o1.getBuild() != null && o2.getBuild() != null)
return (int) (o1.getBuild().getNumber() - o2.getBuild().getNumber());
else
return 0;
}
});
return requirements;
}
}) {
@Override
protected void populateItem(ListItem<BuildRequirement> item) {
BuildRequirement requirement = item.getModelObject();
if (requirement.getBuild() != null) {
item.add(new BuildStatusIcon("icon", new AbstractReadOnlyModel<Build>() {
@Override
public Build getObject() {
return item.getModelObject().getBuild();
}
}));
item.add(new Label("name", requirement.getBuild().getStatus().getDisplayName()));
Link<Void> link = new BookmarkablePageLink<Void>("title", BuildLogPage.class,
BuildLogPage.paramsOf(requirement.getBuild(), null));
StringBuilder builder = new StringBuilder("#" + requirement.getBuild().getNumber());
if (requirement.getBuild().getVersion() != null)
builder.append(" (" + requirement.getBuild().getVersion() + ")");
builder.append(" : ").append(requirement.getBuild().getJobName());
link.add(new Label("label", builder.toString()));
item.add(link);
} else {
WebMarkupContainer icon = new WebMarkupContainer("icon");
icon.add(AttributeAppender.append("class", "build-status build-status-pending fa fa-fw"));
icon.add(AttributeAppender.append("title", "Build is pending"));
item.add(icon);
item.add(new Label("name", "Pending"));
WebMarkupContainer titleLink = new WebMarkupContainer("title") {
@Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
tag.setName("span");
}
};
titleLink.add(new Label("label", requirement.getJobName()));
item.add(titleLink);
}
}
});
setOutputMarkupId(true);
}
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(!getPullRequest().getBuildRequirements().isEmpty());
}
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(CssHeaderItem.forReference(new BuildStatusCssResourceReference()));
}
}

View File

@ -2,16 +2,19 @@ package io.onedev.server.web.component.build.status;
import java.util.Collection;
import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import com.google.common.collect.Sets;
import io.onedev.server.OneDev;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.model.Build;
import io.onedev.server.model.Build.Status;
import io.onedev.server.web.behavior.WebSocketObserver;
@ -19,8 +22,23 @@ import io.onedev.server.web.behavior.WebSocketObserver;
@SuppressWarnings("serial")
public class BuildStatusIcon extends GenericPanel<Build> {
public BuildStatusIcon(String id, IModel<Build> model) {
super(id, model);
private final Long buildId;
private final boolean withTooltip;
public BuildStatusIcon(String id, Long buildId, boolean withTooltip) {
super(id);
this.buildId = buildId;
setModel(new LoadableDetachableModel<Build>() {
@Override
protected Build load() {
return OneDev.getInstance(BuildManager.class).load(buildId);
}
});
this.withTooltip = withTooltip;
}
@Override
@ -35,27 +53,29 @@ public class BuildStatusIcon extends GenericPanel<Build> {
Build build = getModelObject();
String cssClass = "fa fa-fw build-status build-status-" + build.getStatus().name().toLowerCase();
if (build.getStatus() == Status.RUNNING)
Build.Status status = build.getStatus();
String cssClass = "fa fa-fw build-status build-status-" + status.name().toLowerCase();
if (status == Status.RUNNING)
cssClass += " fa-spin";
String title;
if (build.getStatus() == Status.WAITING)
title = "Waiting for completion of dependency builds";
else if (build.getStatus() == Status.QUEUEING)
title = "Build is being queued due to limited capacity";
else
title = "Build is " + build.getStatus().getDisplayName().toLowerCase();
if (withTooltip) {
String title;
if (status == Status.WAITING)
title = "Waiting for completion of dependency builds";
else if (status == Status.QUEUEING)
title = "Queued due to limited capacity";
else
title = StringUtils.capitalize(status.getDisplayName().toLowerCase());
tag.put("title", title);
}
tag.put("class", cssClass);
tag.put("title", title);
}
});
Long buildId = getModelObject().getId();
add(new WebSocketObserver() {
@Override
@ -77,7 +97,7 @@ public class BuildStatusIcon extends GenericPanel<Build> {
setOutputMarkupPlaceholderTag(true);
}
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);

View File

@ -1,7 +1,6 @@
package io.onedev.server.web.component.build.status;
import java.util.Collection;
import java.util.List;
import org.apache.wicket.Component;
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
@ -26,14 +25,14 @@ import io.onedev.server.web.component.link.DropdownLink;
import io.onedev.server.web.websocket.PageDataChanged;
@SuppressWarnings("serial")
public abstract class CommitStatusPanel extends GenericPanel<List<Build>> {
public abstract class CommitStatusPanel extends GenericPanel<Collection<Build>> {
public CommitStatusPanel(String id) {
super(id);
setModel(new LoadableDetachableModel<List<Build>>() {
setModel(new LoadableDetachableModel<Collection<Build>>() {
@Override
protected List<Build> load() {
protected Collection<Build> load() {
return OneDev.getInstance(BuildManager.class).query(getProject(), getCommitId().name());
}

View File

@ -2,7 +2,7 @@
<table class="build-status-list">
<tr wicket:id="builds">
<td class="status">
<span wicket:id="icon"></span> <span wicket:id="name"></span>
<span wicket:id="icon"></span>
</td>
<td class="title">
<a wicket:id="title"><span wicket:id="label"></span></a>

View File

@ -1,5 +1,7 @@
package io.onedev.server.web.component.build.status;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
@ -14,39 +16,21 @@ import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import edu.emory.mathcs.backport.java.util.Collections;
import io.onedev.server.model.Build;
import io.onedev.server.web.page.project.builds.detail.BuildLogPage;
@SuppressWarnings("serial")
public class StatusListPanel extends GenericPanel<List<Build>> {
public class StatusListPanel extends GenericPanel<Collection<Build>> {
public StatusListPanel(String id, IModel<List<Build>> model) {
public StatusListPanel(String id, IModel<Collection<Build>> model) {
super(id, model);
add(new ListView<Build>("builds", new LoadableDetachableModel<List<Build>>() {
@Override
protected List<Build> load() {
List<Build> builds = model.getObject();
if (!builds.isEmpty()) {
List<String> jobNames = builds.iterator().next().getProject().getJobNames();
Collections.sort(builds, new Comparator<Build>() {
@Override
public int compare(Build o1, Build o2) {
int index1 = jobNames.indexOf(o1.getJobName());
int index2 = jobNames.indexOf(o2.getJobName());
if (index1 != index2)
return index1 - index2;
else if (!o1.getJobName().equals(o2.getJobName()))
return o1.getJobName().compareTo(o2.getJobName());
else
return (int) (o1.getNumber() - o2.getNumber());
}
});
}
List<Build> builds = new ArrayList<>(model.getObject());
builds.sort(Comparator.comparing(Build::getNumber));
return builds;
}
@ -54,11 +38,10 @@ public class StatusListPanel extends GenericPanel<List<Build>> {
@Override
protected void populateItem(ListItem<Build> item) {
item.add(new BuildStatusIcon("icon", item.getModel()));
item.add(new Label("name", item.getModel().getObject().getStatus().getDisplayName()));
Build build = item.getModelObject();
item.add(new BuildStatusIcon("icon", build.getId(), true));
Link<Void> buildLink = new BookmarkablePageLink<Void>("title",
BuildLogPage.class, BuildLogPage.paramsOf(build, null));

View File

@ -1,11 +1,17 @@
.build-status {
font-size: 16px;
}
.build-status-running {
font-size: 14px;
}
.build-status-successful:before {
content: "\f00c";
content: "\f058";
}
.build-status-in_error:before {
content: "\f071";
content: "\f06a";
}
.build-status-failed:before {
content: "\f00d";
content: "\f057";
}
.build-status-cancelled:before {
content: "\f05e";
@ -17,10 +23,10 @@
content: "\f1ce";
}
.build-status-waiting:before {
content: "\f04c";
content: "\f28c";
}
.build-status-queueing:before {
content: "\f254";
content: "\f192";
}
.build-status-successful {
color: #06BC06 !important;
@ -43,6 +49,15 @@
.build-status-list {
width: auto;
}
.build-status-list>tbody>tr>td {
padding: 4px 8px;
.build-status-list>tbody>tr>td.status {
padding: 4px 4px 4px 0;
}
.build-status-list>tbody>tr>td.title {
padding: 4px 0 4px 4px;
}
.floating>.content>.build-status-list>tbody>tr>td.status {
padding-left: 8px;
}
.floating>.content>.build-status-list>tbody>tr>td.title {
padding-right: 8px;
}

View File

@ -5,6 +5,7 @@ import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.model.IModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import io.onedev.server.ci.CISpec;
import io.onedev.server.ci.job.Job;
@ -19,14 +20,14 @@ public class JobLink extends BookmarkablePageLink<Void> {
private final IModel<Project> projectModel;
private final String revision;
private final ObjectId commitId;
private final String jobName;
public JobLink(String id, IModel<Project> projectModel, String revision, String jobName) {
public JobLink(String id, IModel<Project> projectModel, ObjectId commitId, String jobName) {
super(id, ProjectBlobPage.class);
this.projectModel = projectModel;
this.revision = revision;
this.commitId = commitId;
this.jobName = jobName;
}
@ -52,7 +53,7 @@ public class JobLink extends BookmarkablePageLink<Void> {
@Override
public PageParameters getPageParameters() {
ProjectBlobPage.State state = new ProjectBlobPage.State();
state.blobIdent = new BlobIdent(revision, CISpec.BLOB_PATH, FileMode.REGULAR_FILE.getBits());
state.blobIdent = new BlobIdent(commitId.name(), CISpec.BLOB_PATH, FileMode.REGULAR_FILE.getBits());
state.position = CISpecRendererProvider.getPosition(Job.SELECTION_PREFIX + jobName);
return ProjectBlobPage.paramsOf(projectModel.getObject(), state);
}

View File

@ -0,0 +1,19 @@
<wicket:panel>
<div class="commit-jobs">
<div wicket:id="jobs" class="job">
<div class="head clearfix">
<a wicket:id="name" class="name pull-left"><span wicket:id="label"></span></a>
<span class="actions pull-right">
<a wicket:id="list" class="list" title="Show builds in list"><i class="fa fa-list-ul"></i></a>
<a wicket:id="run" class="run" title="Run this job"><i class="fa fa-play-circle"></i></a>
</span>
</div>
<div class="body">
<wicket:container wicket:id="builds">
<a wicket:id="build" class="build"><span wicket:id="status"></span></a>
</wicket:container>
<div wicket:id="noBuilds" class="no-builds">No builds</div>
</div>
</div>
</div>
</wicket:panel>

View File

@ -0,0 +1,294 @@
package io.onedev.server.web.component.job.commit;
import static io.onedev.server.search.entity.EntityQuery.quote;
import static io.onedev.server.search.entity.build.BuildQuery.getRuleName;
import static io.onedev.server.search.entity.build.BuildQueryLexer.And;
import static io.onedev.server.search.entity.build.BuildQueryLexer.Is;
import static io.onedev.server.util.BuildConstants.FIELD_COMMIT;
import static io.onedev.server.util.BuildConstants.FIELD_JOB;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.eclipse.jgit.lib.ObjectId;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import edu.emory.mathcs.backport.java.util.Collections;
import io.onedev.server.OneDev;
import io.onedev.server.ci.CISpec;
import io.onedev.server.ci.job.Job;
import io.onedev.server.ci.job.JobManager;
import io.onedev.server.ci.job.param.JobParam;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.model.Build;
import io.onedev.server.model.Build.Status;
import io.onedev.server.model.Project;
import io.onedev.server.security.SecurityUtils;
import io.onedev.server.util.inputspec.InputContext;
import io.onedev.server.util.inputspec.InputSpec;
import io.onedev.server.web.behavior.WebSocketObserver;
import io.onedev.server.web.component.beaneditmodal.BeanEditModalPanel;
import io.onedev.server.web.component.build.status.BuildStatusIcon;
import io.onedev.server.web.component.job.JobLink;
import io.onedev.server.web.page.base.BasePage;
import io.onedev.server.web.page.project.builds.ProjectBuildsPage;
import io.onedev.server.web.page.project.builds.detail.BuildLogPage;
import io.onedev.server.web.util.QueryPosition;
import io.onedev.server.web.websocket.WebSocketManager;
@SuppressWarnings("serial")
public class CommitJobsPanel extends Panel {
private final IModel<Project> projectModel;
private final ObjectId commitId;
private final IModel<List<Build>> buildsModel = new LoadableDetachableModel<List<Build>>() {
@Override
protected List<Build> load() {
List<Build> builds = new ArrayList<>(OneDev.getInstance(BuildManager.class).query(getProject(), commitId.name()));
Collections.sort(builds);
return builds;
}
};
public CommitJobsPanel(String id, IModel<Project> projectModel, ObjectId commitId) {
super(id);
this.projectModel = projectModel;
this.commitId = commitId;
}
private Project getProject() {
return projectModel.getObject();
}
@Override
protected void onInitialize() {
super.onInitialize();
RepeatingView jobsView = new RepeatingView("jobs");
CISpec ciSpec = getProject().getCISpec(commitId);
if (ciSpec != null) {
for (Job job: ciSpec.getSortedJobs()) {
String query = ""
+ quote(FIELD_COMMIT) + " " + getRuleName(Is) + " " + quote(commitId.name())
+ " " + getRuleName(And) + " "
+ quote(FIELD_JOB) + " " + getRuleName(Is) + " " + quote(job.getName());
WebMarkupContainer jobContainer = new WebMarkupContainer(jobsView.newChildId());
jobsView.add(jobContainer);
Link<Void> jobLink = new JobLink("name", projectModel, commitId, job.getName());
jobLink.add(new Label("label", job.getName()));
jobContainer.add(jobLink);
jobContainer.add(new BookmarkablePageLink<Void>("list", ProjectBuildsPage.class,
ProjectBuildsPage.paramsOf(getProject(), query, 0)));
jobContainer.add(new AjaxLink<Void>("run") {
@Override
public void onClick(AjaxRequestTarget target) {
if (!job.getParamSpecs().isEmpty()) {
Serializable paramBean;
try {
paramBean = JobParam.defineBeanClass(job.getParamSpecs()).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
new ParamEditModalPanel(target, paramBean) {
@Override
protected void onSave(AjaxRequestTarget target, Serializable bean) {
Map<String, List<String>> paramMap = JobParam.getParamMap(
job, bean, job.getParamSpecMap().keySet());
Build build = OneDev.getInstance(JobManager.class).submit(getProject(),
commitId.name(), job.getName(), paramMap);
setResponsePage(BuildLogPage.class, BuildLogPage.paramsOf(build, null));
}
@Override
public List<String> getInputNames() {
return new ArrayList<>(job.getParamSpecMap().keySet());
}
@Override
public InputSpec getInputSpec(String inputName) {
return Preconditions.checkNotNull(job.getParamSpecMap().get(inputName));
}
@Override
public void validateName(String inputName) {
throw new UnsupportedOperationException();
}
};
} else {
Build build = OneDev.getInstance(JobManager.class).submit(getProject(), commitId.name(),
job.getName(), new HashMap<>());
setResponsePage(BuildLogPage.class, BuildLogPage.paramsOf(build, null));
}
}
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(SecurityUtils.canWriteCode(getProject().getFacade()));
}
});
IModel<List<Build>> jobBuildsModel = new LoadableDetachableModel<List<Build>>() {
@Override
protected List<Build> load() {
return buildsModel.getObject()
.stream()
.filter(it->it.getJobName().equals(job.getName()))
.collect(Collectors.toList());
}
};
jobContainer.add(new ListView<Build>("builds", jobBuildsModel) {
@Override
protected void populateItem(ListItem<Build> item) {
Build build = item.getModelObject();
QueryPosition position = new QueryPosition(query, getList().size(), item.getIndex());
Link<Void> link = new BookmarkablePageLink<Void>("build", BuildLogPage.class,
BuildLogPage.paramsOf(build, position));
link.add(new BuildStatusIcon("status", build.getId(), false) {
@Override
protected void onInitialize() {
super.onInitialize();
add(AttributeAppender.append("title", new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
Build build = getModelObject();
StringBuilder title = new StringBuilder("Build #" + build.getNumber());
if (build.getVersion() != null)
title.append(" (").append(build.getVersion()).append(")");
title.append(" is ");
if (build.getStatus() == Status.WAITING)
title.append("waiting for completion of dependency builds");
else if (build.getStatus() == Status.QUEUEING)
title.append("queued due to limited capacity");
else
title.append(build.getStatus().getDisplayName().toLowerCase());
return title.append(", click for details").toString();
}
}));
}
});
item.add(link);
}
});
jobContainer.add(new WebMarkupContainer("noBuilds") {
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(jobBuildsModel.getObject().isEmpty());
}
});
}
}
add(jobsView);
add(new WebSocketObserver() {
@Override
public void onObservableChanged(IPartialPageRequestHandler handler, String observable) {
handler.add(component);
}
@Override
public void onConnectionOpened(IPartialPageRequestHandler handler) {
handler.add(component);
}
@Override
public Collection<String> getObservables() {
return Sets.newHashSet("commit-builds:" + getProject().getId() + ":" + commitId.name());
}
});
setOutputMarkupId(true);
}
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(JavaScriptHeaderItem.forReference(new CommitJobsResourceReference()));
String script = String.format("onedev.server.commitJobs.onDomReady('%s');", getMarkupId());
response.render(OnDomReadyHeaderItem.forScript(script));
}
@Override
protected void onConfigure() {
super.onConfigure();
CISpec ciSpec = getProject().getCISpec(commitId);
setVisible(ciSpec != null && !ciSpec.getJobs().isEmpty());
}
@Override
protected void onAfterRender() {
OneDev.getInstance(WebSocketManager.class).notifyObserverChange((BasePage) getPage());
super.onAfterRender();
}
@Override
protected void onDetach() {
projectModel.detach();
buildsModel.detach();
super.onDetach();
}
private abstract class ParamEditModalPanel extends BeanEditModalPanel implements InputContext {
public ParamEditModalPanel(AjaxRequestTarget target, Serializable bean) {
super(target, bean);
}
}
}

View File

@ -0,0 +1,30 @@
package io.onedev.server.web.component.job.commit;
import java.util.List;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.HeaderItem;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import io.onedev.server.web.asset.perfectscrollbar.PerfectScrollbarResourceReference;
import io.onedev.server.web.page.base.BaseDependentCssResourceReference;
import io.onedev.server.web.page.base.BaseDependentResourceReference;
public class CommitJobsResourceReference extends BaseDependentResourceReference {
private static final long serialVersionUID = 1L;
public CommitJobsResourceReference() {
super(CommitJobsResourceReference.class, "commit-jobs.js");
}
@Override
public List<HeaderItem> getDependencies() {
List<HeaderItem> dependencies = super.getDependencies();
dependencies.add(JavaScriptHeaderItem.forReference(new PerfectScrollbarResourceReference()));
dependencies.add(CssHeaderItem.forReference(new BaseDependentCssResourceReference(
CommitJobsResourceReference.class, "commit-jobs.css")));
return dependencies;
}
}

View File

@ -0,0 +1,47 @@
.commit-jobs {
white-space: nowrap;
}
.commit-jobs>.job {
display: inline-block;
margin-right: 20px;
border: 1px solid #BBB;
border-radius: 4px;
width: 200px;
}
.commit-jobs>.job:last-child {
margin-right: 0;
}
.commit-jobs>.job>.head {
padding: 4px 8px;
border-bottom: 1px solid #D8D8D8;
vertical-align: middle;
font-size: 16px;
background: #F9F9F9;
border-radius: 4px 4px 0 0;
font-weight: bold;
}
.commit-jobs>.job>.head>.actions a {
color: #666;
margin: 0 0 0 8px;
}
.commit-jobs>.job>.head>.actions a.run {
line-height: 16px;
font-size: 18px;
}
.commit-jobs>.job>.body {
height: 75px;
padding: 8px 8px 0 8px;
white-space: normal;
}
.commit-jobs>.job>.body .build-status {
margin-bottom: 8px;
font-size: 18px;
}
.commit-jobs>.job>.body .build-status-running {
font-size: 16px;
}
.commit-jobs>.job>.body>.no-builds {
font-size: 16px;
font-style: italic;
margin-bottom: 8px;
}

View File

@ -0,0 +1,11 @@
onedev.server.commitJobs = {
onDomReady: function(containerId) {
$("#" + containerId + ">.commit-jobs>.job>.body").each(function() {
var ps = new PerfectScrollbar(this);
$(window).resize(function() {
ps.update();
});
});
}
}

View File

@ -212,7 +212,7 @@ class ParamListEditPanel extends PropertyEditor<List<Serializable>> {
if (param.getValuesProvider() instanceof SpecifiedValues) {
SpecifiedValues specifiedValues = (SpecifiedValues) param.getValuesProvider();
try {
JobParam.validateValues(specifiedValues.getValues());
JobParam.validateParamValues(specifiedValues.getValues());
} catch (ValidationException e) {
RepeatingView paramsView = (RepeatingView) get("params");
paramsView.get(index).get("values").error(e.getMessage());

View File

@ -12,6 +12,6 @@
.script-support .CodeMirror-scroll {
min-height: 120px;
}
.property.viewer .CodeMirror .CodeMirror-cursor {
.property-viewer .CodeMirror .CodeMirror-cursor {
border-left: none;
}

View File

@ -48,6 +48,7 @@ onedev.server.scriptSupport = {
styleSelectedText: true,
foldGutter: true,
matchBrackets: true,
scrollbarStyle: "simple",
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
highlightIdentifiers: {delay: 500}
});

View File

@ -35,7 +35,7 @@ import com.google.common.collect.Sets;
import io.onedev.server.OneDev;
import io.onedev.server.OneException;
import io.onedev.server.ci.job.JobScheduler;
import io.onedev.server.ci.job.JobManager;
import io.onedev.server.ci.job.param.JobParam;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.git.GitUtils;
@ -152,7 +152,7 @@ public abstract class BuildDetailPage extends ProjectPage implements InputContex
}));
summary.add(new BuildStatusIcon("statusIcon", buildModel));
summary.add(new BuildStatusIcon("statusIcon", getBuild().getId(), true));
summary.add(new Label("statusLabel", new AbstractReadOnlyModel<String>() {
@Override
@ -167,9 +167,8 @@ public abstract class BuildDetailPage extends ProjectPage implements InputContex
private void resubmit(Serializable paramBean) {
Map<String, List<String>> paramMap = JobParam.getParamMap(getBuild().getJob(), paramBean,
getBuild().getJob().getParamSpecMap().keySet());
OneDev.getInstance(JobScheduler.class).resubmit(getBuild(), paramMap);
OneDev.getInstance(JobManager.class).resubmit(getBuild(), paramMap);
setResponsePage(BuildLogPage.class, BuildLogPage.paramsOf(getBuild(), position));
getSession().success("Rebuild request submitted");
}
@Override
@ -232,7 +231,7 @@ public abstract class BuildDetailPage extends ProjectPage implements InputContex
@Override
public void onClick() {
OneDev.getInstance(JobScheduler.class).cancel(getBuild());
OneDev.getInstance(JobManager.class).cancel(getBuild());
getSession().success("Cancel request submitted");
}

View File

@ -8,6 +8,13 @@
position: relative;
font-size: 24px;
}
#build-detail>.main>.summary .build-status {
font-size: 24px;
}
#build-detail>.main>.summary .build-status-running {
font-size: 22px;
}
#build-detail>.main>.summary>* {
margin-right: 20px;
}

View File

@ -19,13 +19,13 @@
<span wicket:id="contribution" class="name"></span>
</div>
<div class="commit pull-left">
<span wicket:id="buildStatus" class="build-status"></span>
<span wicket:id="hash" class="hash"></span>
<a wicket:id="copyHash" class="copy"><i class="fa fa-clipboard"></i></a>
</div>
<div wicket:id="parents" class="parents pull-left"></div>
</div>
</div>
</div>
<div wicket:id="jobs"></div>
<div wicket:id="revisionDiff"></div>
</div>
<wicket:fragment wicket:id="refsFrag">

View File

@ -53,11 +53,11 @@ import io.onedev.server.util.CommitMessageTransformer;
import io.onedev.server.util.diff.WhitespaceOption;
import io.onedev.server.web.behavior.clipboard.CopyClipboardBehavior;
import io.onedev.server.web.component.branch.create.CreateBranchLink;
import io.onedev.server.web.component.build.status.CommitStatusPanel;
import io.onedev.server.web.component.contributorpanel.ContributorPanel;
import io.onedev.server.web.component.createtag.CreateTagLink;
import io.onedev.server.web.component.diff.revision.CommentSupport;
import io.onedev.server.web.component.diff.revision.RevisionDiffPanel;
import io.onedev.server.web.component.job.commit.CommitJobsPanel;
import io.onedev.server.web.component.link.ViewStateAwarePageLink;
import io.onedev.server.web.component.user.contributoravatars.ContributorAvatars;
import io.onedev.server.web.page.project.ProjectPage;
@ -218,25 +218,13 @@ public class CommitDetailPage extends ProjectPage implements CommentSupport {
add(new ContributorAvatars("contributorAvatars", getCommit().getAuthorIdent(), getCommit().getCommitterIdent()));
add(new ContributorPanel("contribution", getCommit().getAuthorIdent(), getCommit().getCommitterIdent()));
add(new CommitStatusPanel("buildStatus") {
@Override
protected Project getProject() {
return CommitDetailPage.this.getProject();
}
@Override
protected ObjectId getCommitId() {
return CommitDetailPage.this.getCommit().copy();
}
});
add(new Label("hash", GitUtils.abbreviateSHA(getCommit().name())));
add(new WebMarkupContainer("copyHash").add(new CopyClipboardBehavior(Model.of(getCommit().name()))));
newParentsContainer(null);
add(new CommitJobsPanel("jobs", projectModel, getCommit().copy()));
newRevisionDiff(null);
}

View File

@ -4,7 +4,7 @@
<span class="pull-left">Backlog</span>
<span wicket:id="count" class="badge pull-left"></span>
<a wicket:id="addCard" title="Add new card to this column" class="add-card pull-right"><i class="fa fa-plus"></i></a>
<a wicket:id="viewAsList" title="View as issue list" class="view-list pull-right"><i class="fa fa-list-ul"></i></a>
<a wicket:id="viewAsList" title="Show issues in list" class="view-list pull-right"><i class="fa fa-list-ul"></i></a>
</div>
<div wicket:id="body" class="body"></div>
</div>

View File

@ -5,7 +5,7 @@
<span wicket:id="title" class="pull-left"></span>
<span wicket:id="count" class="badge pull-left"></span>
<a wicket:id="addCard" title="Add new card to this column" class="add-card pull-right"><i class="fa fa-plus"></i></a>
<a wicket:id="viewAsList" title="View as issue list" class="show-list pull-right"><i class="fa fa-list-ul"></i></a>
<a wicket:id="viewAsList" title="Show issues in list" class="show-list pull-right"><i class="fa fa-list-ul"></i></a>
</div>
<div wicket:id="body" class="body"></div>
</div>

View File

@ -88,12 +88,6 @@
<td class="name">Reviewers</td>
<td class="value"><div wicket:id="reviewers"></div></td>
</tr>
<wicket:enclosure child="builds">
<tr class="builds">
<td class="name">Builds</td>
<td class="value"><div wicket:id="builds"></div></td>
</tr>
</wicket:enclosure>
</table>
<div class="clearfix">
<input wicket:id="send" type="submit" class="btn btn-primary pull-left" value="Create Pull Request">

View File

@ -65,7 +65,6 @@ import io.onedev.server.util.diff.WhitespaceOption;
import io.onedev.server.web.ajaxlistener.DisableGlobalLoadingIndicatorListener;
import io.onedev.server.web.component.branch.BranchLink;
import io.onedev.server.web.component.branch.picker.AffinalBranchPicker;
import io.onedev.server.web.component.build.status.BuildRequirementListPanel;
import io.onedev.server.web.component.commit.list.CommitListPanel;
import io.onedev.server.web.component.diff.revision.CommentSupport;
import io.onedev.server.web.component.diff.revision.RevisionDiffPanel;
@ -633,8 +632,6 @@ public class NewPullRequestPage extends ProjectPage implements CommentSupport {
form.add(newMergeStrategyContainer());
form.add(new ReviewListPanel("reviewers", requestModel));
form.add(new BuildRequirementListPanel("builds", requestModel));
return fragment;
}

View File

@ -15,6 +15,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.persistence.EntityNotFoundException;
@ -72,6 +73,7 @@ import io.onedev.server.entitymanager.PullRequestManager;
import io.onedev.server.entitymanager.PullRequestUpdateManager;
import io.onedev.server.entitymanager.PullRequestWatchManager;
import io.onedev.server.model.AbstractEntity;
import io.onedev.server.model.Build;
import io.onedev.server.model.Project;
import io.onedev.server.model.PullRequest;
import io.onedev.server.model.PullRequestUpdate;
@ -88,7 +90,7 @@ import io.onedev.server.util.facade.UserFacade;
import io.onedev.server.util.userident.UserIdent;
import io.onedev.server.web.ajaxlistener.ConfirmLeaveListener;
import io.onedev.server.web.component.branch.BranchLink;
import io.onedev.server.web.component.build.status.BuildRequirementListPanel;
import io.onedev.server.web.component.build.status.StatusListPanel;
import io.onedev.server.web.component.entity.nav.EntityNavPanel;
import io.onedev.server.web.component.entity.watches.EntityWatchesPanel;
import io.onedev.server.web.component.floating.FloatingPanel;
@ -452,7 +454,22 @@ public abstract class PullRequestDetailPage extends ProjectPage {
fragment.add(newMergeStrategyContainer());
fragment.add(new ReviewListPanel("reviews", requestModel));
fragment.add(new BuildRequirementListPanel("builds", requestModel));
fragment.add(new StatusListPanel("builds", new LoadableDetachableModel<Collection<Build>>() {
@Override
protected Collection<Build> load() {
return getPullRequest().getPullRequestBuilds().stream().map(it->it.getBuild()).collect(Collectors.toList());
}
}) {
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(!getPullRequest().getPullRequestBuilds().isEmpty());
}
});
fragment.add(new EntityWatchesPanel("watches") {

View File

@ -1,2 +1,3 @@
<wicket:extend>
<a wicket:id="test">test</a>
</wicket:extend>

View File

@ -3,6 +3,7 @@ package io.onedev.server.web.page.test;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import io.onedev.server.web.page.base.BasePage;
@ -17,6 +18,20 @@ public class TestPage extends BasePage {
@Override
protected void onInitialize() {
super.onInitialize();
add(new Link<Void>("test") {
@Override
public void onClick() {
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
}
});
}
@Override

View File

@ -5,6 +5,7 @@ import javax.inject.Singleton;
import io.onedev.commons.launcher.loader.Listen;
import io.onedev.server.event.build.BuildEvent;
import io.onedev.server.event.build.BuildSubmitted;
import io.onedev.server.model.Build;
import io.onedev.server.web.util.WicketUtils;
@ -20,8 +21,13 @@ public class BuildEventBroadcaster {
@Listen
public void on(BuildEvent event) {
webSocketManager.notifyObservableChange(Build.getWebSocketObservable(event.getBuild().getId()), WicketUtils.getPageKey());
webSocketManager.notifyObservableChange("commit-status:" + event.getBuild().getCommitHash(), WicketUtils.getPageKey());
PageKey pageKey = WicketUtils.getPageKey();
webSocketManager.notifyObservableChange(Build.getWebSocketObservable(event.getBuild().getId()), pageKey);
webSocketManager.notifyObservableChange("commit-status:" + event.getBuild().getCommitHash(), pageKey);
if (event instanceof BuildSubmitted) {
String observable = "commit-builds:" + event.getProject().getId() + ":" + event.getBuild().getCommitHash();
webSocketManager.notifyObservableChange(observable, pageKey);
}
}
}