chore: Add MCP build support

This commit is contained in:
Robin Shen 2025-09-07 14:53:51 +08:00
parent 2a1a2a5a43
commit 2cb6b6b7fa
11 changed files with 373 additions and 91 deletions

View File

@ -40,6 +40,8 @@ import com.google.common.base.Splitter;
import io.onedev.commons.utils.StringUtils;
import io.onedev.server.SubscriptionManager;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.BuildParamManager;
import io.onedev.server.entitymanager.IssueChangeManager;
import io.onedev.server.entitymanager.IssueCommentManager;
import io.onedev.server.entitymanager.IssueLinkManager;
@ -57,13 +59,17 @@ import io.onedev.server.entitymanager.PullRequestManager;
import io.onedev.server.entitymanager.PullRequestReviewManager;
import io.onedev.server.entitymanager.SettingManager;
import io.onedev.server.entitymanager.UserManager;
import io.onedev.server.entityreference.BuildReference;
import io.onedev.server.entityreference.IssueReference;
import io.onedev.server.entityreference.PullRequestReference;
import io.onedev.server.exception.InvalidIssueFieldsException;
import io.onedev.server.exception.InvalidReferenceException;
import io.onedev.server.exception.IssueLinkValidationException;
import io.onedev.server.exception.PullRequestReviewRejectedException;
import io.onedev.server.git.GitUtils;
import io.onedev.server.git.service.GitService;
import io.onedev.server.job.JobManager;
import io.onedev.server.model.Build;
import io.onedev.server.model.Issue;
import io.onedev.server.model.IssueComment;
import io.onedev.server.model.IssueLink;
@ -96,6 +102,7 @@ import io.onedev.server.model.support.pullrequest.changedata.PullRequestRequeste
import io.onedev.server.rest.annotation.Api;
import io.onedev.server.rest.resource.support.RestConstants;
import io.onedev.server.search.entity.EntityQuery;
import io.onedev.server.search.entity.build.BuildQuery;
import io.onedev.server.search.entity.issue.IssueQuery;
import io.onedev.server.search.entity.issue.IssueQueryParseOption;
import io.onedev.server.search.entity.pullrequest.PullRequestQuery;
@ -148,6 +155,12 @@ public class McpHelperResource {
private final PullRequestCommentManager pullRequestCommentManager;
private final BuildManager buildManager;
private final BuildParamManager buildParamManager;
private final JobManager jobManager;
private final GitService gitService;
private final LabelSpecManager labelSpecManager;
@ -165,7 +178,8 @@ public class McpHelperResource {
LabelSpecManager labelSpecManager, PullRequestReviewManager pullRequestReviewManager,
PullRequestAssignmentManager pullRequestAssignmentManager,
PullRequestLabelManager pullRequestLabelManager, UrlManager urlManager,
PullRequestCommentManager pullRequestCommentManager) {
PullRequestCommentManager pullRequestCommentManager, BuildManager buildManager,
BuildParamManager buildParamManager, JobManager jobManager) {
this.objectMapper = objectMapper;
this.settingManager = settingManager;
this.issueManager = issueManager;
@ -187,6 +201,9 @@ public class McpHelperResource {
this.pullRequestLabelManager = pullRequestLabelManager;
this.urlManager = urlManager;
this.pullRequestCommentManager = pullRequestCommentManager;
this.buildManager = buildManager;
this.buildParamManager = buildParamManager;
this.jobManager = jobManager;
}
private String getIssueQueryStringDescription() {
@ -369,6 +386,57 @@ public class McpHelperResource {
return HtmlEscape.escapeHtml5(description);
}
private String getBuildQueryStringDescription() {
var orderFields = new StringBuilder();
for (var field : Build.SORT_FIELDS.keySet()) {
orderFields.append("- ").append(field).append("\n");
}
var jobNames = buildManager.getJobNames(null).stream().collect(Collectors.joining(", "));
var paramNames = buildParamManager.getParamNames(null).stream().collect(Collectors.joining(", "));
var labelNames = labelSpecManager.query().stream().map(LabelSpec::getName).collect(Collectors.joining(", "));
var description =
"A query string is one of below criteria:\n" +
"- build with specified number in form of: \"Number\" is \"#<build number>\", or in form of: \"Number\" is \"<project key>-<build number>\" (quotes are required)\n" +
"- criteria to check if version/job contains specified text in form of: ~<containing text>~\n" +
"- sucessful criteria in form of: sucessful\n" +
"- failed criteria in form of: failed\n" +
"- cancelled criteria in form of: cancelled\n" +
"- timed out criteria in form of: timed out\n" +
"- finished criteria in form of: finished\n" +
"- running criteria in form of: running\n" +
"- waiting criteria in form of: waiting\n" +
"- pending criteria in form of: pending\n" +
"- submitted by specified user criteria in form of: submitted by \"<login name of a user>\" (quotes are required)\n" +
"- submitted by current user criteria in form of: submitted by me (quotes are required)\n" +
"- cancelled by specified user criteria in form of: cancelled by \"<login name of a user>\" (quotes are required)\n" +
"- cancelled by current user criteria in form of: cancelled by me (quotes are required)\n" +
"- depends on specified build criteria in form of: depends on \"<build reference>\" (quotes are required)\n" +
"- dependencies of specified build criteria in form of: dependencies of \"<build reference>\" (quotes are required)\n" +
"- fixed specified issue criteria in form of: fixed issue \"<issue reference>\" (quotes are required)\n" +
"- job criteria in form of: \"Job\" is \"<job name>\" (quotes are required), where <job name> is one of: " + jobNames + "\n" +
"- version criteria in form of: \"Version\" is \"<version>\" (quotes are required)\n" +
"- branch criteria in form of: \"Branch\" is \"<branch name>\" (quotes are required)\n" +
"- tag criteria in form of: \"Tag\" is \"<tag name>\" (quotes are required)\n" +
"- param criteria in form of: \"<param name>\" is \"<param value>\" (quotes are required), where <param name> is one of: " + paramNames + "\n" +
"- label criteria in form of: \"Label\" is \"<label name>\" (quotes are required), where <label name> is one of: " + labelNames + "\n" +
"- pull request criteria in form of: \"Pull Request\" is \"<pull request reference>\" (quotes are required)\n" +
"- commit criteria in form of: \"Commit\" is \"<commit hash>\" (quotes are required)\n" +
"- before certain date criteria in form of: \"Submit Date\" is until \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
"- after certain date criteria in form of: \"Submit Date\" is since \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
"- and criteria in form of <criteria1> and <criteria2>\n" +
"- or criteria in form of <criteria1> or <criteria2>. Note that \"and criteria\" takes precedence over \"or criteria\", use braces to group \"or criteria\" like \"(criteria1 or criteria2) and criteria3\" if you want to override precedence\n" +
"- not criteria in form of not(<criteria>)\n" +
"\n" +
"And can optionally add order clause at end of query string in form of: order by \"<field1>\" <asc|desc>,\"<field2>\" <asc|desc>,... (quotes are required), where <field> is one of below:\n" +
orderFields +
"\n" +
"Leave empty to list all pull requests";
return HtmlEscape.escapeHtml5(description);
}
private String getToolParamName(String fieldName) {
return fieldName.replace(" ", "_");
}
@ -649,6 +717,28 @@ public class McpHelperResource {
inputSchemas.put("queryPullRequests", queryPullRequestsInputSchema);
var queryBuildsInputSchema = new HashMap<String, Object>();
var queryBuildsProperties = new HashMap<String, Object>();
queryBuildsProperties.put("project", Map.of(
"type", "string",
"description", "Project to query builds in. Leave empty to query in current project"));
queryBuildsProperties.put("query", Map.of(
"type", "string",
"description", getBuildQueryStringDescription()));
queryBuildsProperties.put("offset", Map.of(
"type", "integer",
"description", "start position for the query (optional, defaults to 0)"));
queryBuildsProperties.put("count", Map.of(
"type", "integer",
"description", "number of builds to return (optional, defaults to 25, max 100)"));
queryBuildsInputSchema.put("Type", "object");
queryBuildsInputSchema.put("Properties", queryBuildsProperties);
queryBuildsInputSchema.put("Required", new ArrayList<>());
inputSchemas.put("queryBuilds", queryBuildsInputSchema);
var createPullRequestInputSchema = new HashMap<String, Object>();
var createPullRequestProperties = new HashMap<String, Object>();
createPullRequestProperties.put("targetProject", Map.of(
@ -869,7 +959,6 @@ public class McpHelperResource {
issueMap.put("reference", issue.getReference().toString(currentProject));
issueMap.remove("submitterId");
issueMap.put("submitter", issue.getSubmitter().getName());
issueMap.remove("projectId");
issueMap.put("Project", issue.getProject().getPath());
issueMap.remove("lastActivity");
for (var it = issueMap.entrySet().iterator(); it.hasNext();) {
@ -1251,9 +1340,7 @@ public class McpHelperResource {
pullRequestMap.put("reference", pullRequest.getReference().toString(currentProject));
pullRequestMap.remove("submitterId");
pullRequestMap.put("submitter", pullRequest.getSubmitter().getName());
pullRequestMap.remove("targetProjectId");
pullRequestMap.put("targetProject", pullRequest.getTarget().getProject().getPath());
pullRequestMap.remove("sourceProjectId");
if (pullRequest.getSourceProject() != null)
pullRequestMap.put("sourceProject", pullRequest.getSourceProject().getPath());
pullRequestMap.remove("codeCommentsUpdateDate");
@ -1301,6 +1388,82 @@ public class McpHelperResource {
return pullRequests;
}
@Path("/query-builds")
@GET
public List<Map<String, Object>> queryBuilds(
@QueryParam("currentProject") @NotNull String currentProjectPath,
@QueryParam("project") String projectPath,
@QueryParam("query") String query,
@QueryParam("offset") int offset,
@QueryParam("count") int count) {
if (SecurityUtils.getAuthUser() == null)
throw new UnauthenticatedException();
var projectInfo = getProjectInfo(projectPath, currentProjectPath);
if (count > RestConstants.MAX_PAGE_SIZE)
throw new NotAcceptableException("Count should not be greater than " + RestConstants.MAX_PAGE_SIZE);
EntityQuery<Build> parsedQuery;
if (query != null) {
parsedQuery = BuildQuery.parse(projectInfo.project, query, true, true);
} else {
parsedQuery = new BuildQuery();
}
var builds = new ArrayList<Map<String, Object>>();
for (var build : buildManager.query(projectInfo.project, parsedQuery, false, offset, count)) {
var buildMap = getBuildMap(projectInfo.currentProject, build);
buildMap.put("link", urlManager.urlFor(build, true));
builds.add(buildMap);
}
return builds;
}
@Path("/get-build")
@GET
public Map<String, Object> getBuild(
@QueryParam("currentProject") @NotNull String currentProjectPath,
@QueryParam("reference") @NotNull String buildReference) {
if (SecurityUtils.getAuthUser() == null)
throw new UnauthenticatedException();
var currentProject = getProject(currentProjectPath);
var build = getBuild(currentProject, buildReference);
var buildMap = getBuildMap(currentProject, build);
buildMap.put("params", build.getParamMap());
buildMap.put("labels", build.getLabels().stream().map(it->it.getSpec().getName()).collect(Collectors.toList()));
buildMap.put("link", urlManager.urlFor(build, true));
return buildMap;
}
@Path("/get-previous-successful-build")
@GET
public Map<String, Object> getPreviousSuccessfulBuild(
@QueryParam("currentProject") @NotNull String currentProjectPath,
@QueryParam("reference") @NotNull String buildReference) {
if (SecurityUtils.getAuthUser() == null)
throw new UnauthenticatedException();
var currentProject = getProject(currentProjectPath);
var build = getBuild(currentProject, buildReference);
var previousSuccessfulBuild = buildManager.findStreamPrevious(build, Build.Status.SUCCESSFUL);
if (previousSuccessfulBuild != null) {
var buildMap = getBuildMap(currentProject, previousSuccessfulBuild);
buildMap.put("params", previousSuccessfulBuild.getParamMap());
buildMap.put("labels", previousSuccessfulBuild.getLabels().stream().map(it->it.getSpec().getName()).collect(Collectors.toList()));
buildMap.put("link", urlManager.urlFor(previousSuccessfulBuild, true));
return buildMap;
} else {
throw new NotFoundException("Previous successful build not found");
}
}
@Path("/get-pull-request")
@GET
public Map<String, Object> getPullRequest(
@ -1836,13 +1999,133 @@ public class McpHelperResource {
return "Commented on pull request " + pullRequestReference;
}
private Map<String, Object> getBuildMap(Project currentProject, Build build) {
var typeReference = new TypeReference<LinkedHashMap<String, Object>>() {};
var buildMap = objectMapper.convertValue(build, typeReference);
buildMap.remove("id");
buildMap.remove("uuid");
buildMap.remove("numberScopeId");
buildMap.remove("workspacePath");
buildMap.remove("checkoutPaths");
buildMap.remove("submitSequence");
buildMap.remove("finishTimeGroups");
buildMap.put("reference", build.getReference().toString(currentProject));
buildMap.remove("submitterId");
buildMap.put("submitter", build.getSubmitter().getName());
buildMap.remove("cancellerId");
if (build.getCanceller() != null)
buildMap.put("canceller", build.getCanceller().getName());
buildMap.remove("requestId");
if (build.getRequest() != null)
buildMap.put("pullRequest", build.getRequest().getReference().toString(currentProject));
buildMap.remove("issueId");
if (build.getIssue() != null)
buildMap.put("issue", build.getIssue().getReference().toString(currentProject));
buildMap.remove("agentId");
if (build.getAgent() != null)
buildMap.put("agent", build.getAgent().getName());
buildMap.put("project", build.getProject().getPath());
return buildMap;
}
@SuppressWarnings("unchecked")
@Path("/run-job")
@POST
public Map<String, Object> runJob(
@QueryParam("currentProject") @NotNull String currentProjectPath,
@QueryParam("project") String projectPath,
@NotNull @Valid Map<String, Serializable> data) {
if (SecurityUtils.getAuthUser() == null)
throw new UnauthenticatedException();
var projectInfo = getProjectInfo(projectPath, currentProjectPath);
var jobName = StringUtils.trimToNull((String)data.get("jobName"));
if (jobName == null)
throw new NotAcceptableException("Job name is required");
var project = projectInfo.project;
if (!SecurityUtils.canRunJob(project, jobName))
throw new UnauthorizedException();
String refName;
var branch = StringUtils.trimToNull((String)data.get("branch"));
var tag = StringUtils.trimToNull((String)data.get("tag"));
var commitHash = StringUtils.trimToNull((String)data.get("commitHash"));
if (commitHash != null) {
refName = StringUtils.trimToNull((String)data.get("refName"));
if (refName == null) {
throw new NotAcceptableException("Ref name is required when commit hash is specified");
}
} else if (branch != null) {
refName = GitUtils.branch2ref(branch);
} else if (tag != null) {
refName = GitUtils.tag2ref(tag);
} else {
throw new NotAcceptableException("Either commit hash, branch or tag should be specified");
}
if (commitHash == null)
commitHash = project.getRevCommit(refName, true).name();
Map<String, List<String>> params;
var paramData = data.get("params");
if (paramData instanceof List) {
params = new HashMap<String, List<String>>();
List<String> paramPairs = (List<String>) paramData;
if (paramPairs != null) {
for (var paramPair: paramPairs) {
var paramName = StringUtils.trimToNull(StringUtils.substringBefore(paramPair, "="));
var paramValue = StringUtils.trimToNull(StringUtils.substringAfter(paramPair, "="));
if (paramName != null && paramValue != null)
params.computeIfAbsent(paramName, k -> new ArrayList<>()).add(paramValue);
}
}
} else if (paramData instanceof Map) {
params = (Map<String, List<String>>) paramData;
} else {
params = new HashMap<String, List<String>>();
}
var reason = StringUtils.trimToNull((String)data.get("reason"));
if (reason == null)
throw new NotAcceptableException("Reason is required");
Build build = jobManager.submit(project, ObjectId.fromString(commitHash), jobName,
params, refName, SecurityUtils.getUser(), null,
null, reason);
if (build.isFinished())
jobManager.resubmit(build, reason);
var buildMap = getBuildMap(projectInfo.currentProject, build);
buildMap.put("id", build.getId());
return buildMap;
}
private Build getBuild(Project currentProject, String referenceString) {
BuildReference buildReference;
try {
buildReference = BuildReference.of(referenceString, currentProject);
} catch (InvalidReferenceException e) {
throw new NotAcceptableException(e.getMessage());
}
var build = buildManager.find(buildReference.getProject(), buildReference.getNumber());
if (build != null) {
if (!SecurityUtils.canAccessBuild(build))
throw new UnauthorizedException("No permission to access build: " + referenceString);
return build;
} else {
throw new NotFoundException("Build not found: " + referenceString);
}
}
private void normalizePullRequestData(Map<String, Serializable> data) {
for (var entry : data.entrySet()) {
if (entry.getValue() instanceof String)
entry.setValue(StringUtils.trimToNull((String) entry.getValue()));
}
}
}
private NotAcceptableException newNotAcceptableException(InvalidIssueFieldsException e) {
var invaliParams = new ArrayList<String>();

View File

@ -4,7 +4,6 @@ import java.util.Collection;
import javax.annotation.Nullable;
import io.onedev.server.model.Build;
import io.onedev.server.model.BuildParam;
import io.onedev.server.model.Project;
import io.onedev.server.persistence.dao.EntityManager;
@ -13,8 +12,6 @@ public interface BuildParamManager extends EntityManager<BuildParam> {
void create(BuildParam param);
void deleteParams(Build build);
Collection<String> getParamNames(@Nullable Project project);
}

View File

@ -45,8 +45,6 @@ import javax.inject.Singleton;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
@ -322,7 +320,9 @@ public class DefaultBuildManager extends BaseEntityManager<Build> implements Bui
predicates.add(builder.isNull(root.get(Build.PROP_ISSUE)));
}
predicates.addAll(getPredicates(root, builder, params));
var paramPredicate = getPredicate(root, builder, params);
if (paramPredicate != null)
predicates.add(paramPredicate);
query.where(predicates.toArray(new Predicate[0]));
return getSession().createQuery(query).list();
@ -377,31 +377,53 @@ public class DefaultBuildManager extends BaseEntityManager<Build> implements Bui
predicates.add(builder.isNull(root.get(Build.PROP_ISSUE)));
}
predicates.addAll(getPredicates(root, builder, params));
var paramPredicate = getPredicate(root, builder, params);
if (paramPredicate != null)
predicates.add(paramPredicate);
query.where(predicates.toArray(new Predicate[0]));
return getSession().createQuery(query).list();
}
private List<Predicate> getPredicates(From<Build, Build> root, CriteriaBuilder builder,
Map<String, List<String>> params) {
List<Predicate> predicates = new ArrayList<>();
for (Map.Entry<String, List<String>> entry: params.entrySet()) {
if (!entry.getValue().isEmpty()) {
for (String value: entry.getValue()) {
Join<?, ?> join = root.join(Build.PROP_PARAMS, JoinType.INNER);
predicates.add(builder.equal(join.get(BuildParam.PROP_NAME), entry.getKey()));
predicates.add(builder.equal(join.get(BuildParam.PROP_VALUE), value));
@Nullable
private Predicate getPredicate(From<Build, Build> root, CriteriaBuilder builder, Map<String, List<String>> params) {
if (!params.isEmpty()) {
// Create a single exists subquery to check if build has all required params with same name and values
var subquery = builder.createQuery().subquery(Long.class);
var paramRoot = subquery.from(BuildParam.class);
subquery.select(builder.count(paramRoot));
List<Predicate> paramPredicates = new ArrayList<>();
int expectedParamCount = 0;
for (Map.Entry<String, List<String>> entry: params.entrySet()) {
if (!entry.getValue().isEmpty()) {
for (String value: entry.getValue()) {
paramPredicates.add(builder.and(
builder.equal(paramRoot.get(BuildParam.PROP_NAME), entry.getKey()),
builder.equal(paramRoot.get(BuildParam.PROP_VALUE), value)
));
expectedParamCount++;
}
} else {
paramPredicates.add(builder.and(
builder.equal(paramRoot.get(BuildParam.PROP_NAME), entry.getKey()),
builder.isNull(paramRoot.get(BuildParam.PROP_VALUE))
));
expectedParamCount++;
}
} else {
Join<?, ?> join = root.join(Build.PROP_PARAMS, JoinType.INNER);
predicates.add(builder.equal(join.get(BuildParam.PROP_NAME), entry.getKey()));
predicates.add(builder.isNull(join.get(BuildParam.PROP_VALUE)));
}
subquery.where(
builder.equal(paramRoot.get(BuildParam.PROP_BUILD), root),
builder.or(paramPredicates.toArray(new Predicate[0]))
);
// Check that the count of matching params equals the expected count
return builder.equal(subquery, (long) expectedParamCount);
} else {
return null;
}
return predicates;
}
@Sessional

View File

@ -1,13 +1,27 @@
package io.onedev.server.entitymanager.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.hibernate.query.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import io.onedev.server.cluster.ClusterManager;
import io.onedev.server.entitymanager.BuildParamManager;
import io.onedev.server.event.Listen;
import io.onedev.server.event.entity.EntityPersisted;
import io.onedev.server.event.entity.EntityRemoved;
import io.onedev.server.event.system.SystemStarting;
import io.onedev.server.model.Build;
import io.onedev.server.model.BuildParam;
import io.onedev.server.model.Project;
import io.onedev.server.persistence.TransactionManager;
@ -15,14 +29,6 @@ import io.onedev.server.persistence.annotation.Sessional;
import io.onedev.server.persistence.annotation.Transactional;
import io.onedev.server.persistence.dao.BaseEntityManager;
import io.onedev.server.persistence.dao.Dao;
import org.hibernate.query.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
@Singleton
public class DefaultBuildParamManager extends BaseEntityManager<BuildParam> implements BuildParamManager {
@ -42,15 +48,6 @@ public class DefaultBuildParamManager extends BaseEntityManager<BuildParam> impl
this.clusterManager = clusterManager;
}
@Transactional
@Override
public void deleteParams(Build build) {
Query<?> query = getSession().createQuery("delete from BuildParam where build = :build");
query.setParameter("build", build);
query.executeUpdate();
build.getParams().clear();
}
@SuppressWarnings("unchecked")
@Sessional
@Listen

View File

@ -13,6 +13,11 @@ public class InvalidIssueFieldsException extends ExplicitException {
this(buildMessage(invalidFields), invalidFields);
}
public InvalidIssueFieldsException(String message, Map<String, String> invalidFields) {
super(message);
this.invalidFields = invalidFields;
}
public static String buildMessage(Map<String, String> invalidFields) {
var fieldErrors = new ArrayList<String>();
for (var entry: invalidFields.entrySet()) {
@ -21,11 +26,6 @@ public class InvalidIssueFieldsException extends ExplicitException {
return "Invalid fields: " + String.join(", ", fieldErrors);
}
public InvalidIssueFieldsException(String message, Map<String, String> invalidFields) {
super(message);
this.invalidFields = invalidFields;
}
public Map<String, String> getInvalidFields() {
return invalidFields;
}

View File

@ -106,7 +106,6 @@ import io.onedev.server.cluster.ClusterManager;
import io.onedev.server.cluster.ClusterTask;
import io.onedev.server.entitymanager.AccessTokenManager;
import io.onedev.server.entitymanager.BuildManager;
import io.onedev.server.entitymanager.BuildParamManager;
import io.onedev.server.entitymanager.IssueManager;
import io.onedev.server.entitymanager.ProjectManager;
import io.onedev.server.entitymanager.PullRequestManager;
@ -219,8 +218,6 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
private final ExecutorService executorService;
private final BuildParamManager buildParamManager;
private final TaskScheduler taskScheduler;
private final Validator validator;
@ -253,10 +250,10 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
public DefaultJobManager(BuildManager buildManager, AccessTokenManager accessTokenManager, UserManager userManager,
ListenerRegistry listenerRegistry, SettingManager settingManager, TransactionManager transactionManager,
LogManager logManager, ExecutorService executorService, SessionManager sessionManager,
BuildParamManager buildParamManager, ProjectManager projectManager, Validator validator,
TaskScheduler taskScheduler, ClusterManager clusterManager,
PullRequestManager pullRequestManager, IssueManager issueManager, GitService gitService,
SSLFactory sslFactory, Dao dao, BatchWorkManager batchWorkManager, WorkExecutor workExecutor) {
ProjectManager projectManager, Validator validator, TaskScheduler taskScheduler,
ClusterManager clusterManager, PullRequestManager pullRequestManager, IssueManager issueManager,
GitService gitService, SSLFactory sslFactory, Dao dao, BatchWorkManager batchWorkManager,
WorkExecutor workExecutor) {
this.dao = dao;
this.settingManager = settingManager;
this.buildManager = buildManager;
@ -267,7 +264,6 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
this.logManager = logManager;
this.executorService = executorService;
this.sessionManager = sessionManager;
this.buildParamManager = buildParamManager;
this.projectManager = projectManager;
this.validator = validator;
this.taskScheduler = taskScheduler;
@ -886,31 +882,6 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
build.setAgent(null);
build.getCheckoutPaths().clear();
buildParamManager.deleteParams(build);
for (Map.Entry<String, List<String>> entry : build.getParamMap().entrySet()) {
ParamSpec paramSpec = build.getJob().getParamSpecMap().get(entry.getKey());
checkNotNull(paramSpec);
String type = paramSpec.getType();
List<String> values = entry.getValue();
if (!values.isEmpty()) {
for (String value : values) {
BuildParam param = new BuildParam();
param.setBuild(build);
param.setName(entry.getKey());
param.setType(type);
param.setValue(value);
build.getParams().add(param);
buildParamManager.create(param);
}
} else {
BuildParam param = new BuildParam();
param.setBuild(build);
param.setName(paramSpec.getName());
param.setType(type);
build.getParams().add(param);
buildParamManager.create(param);
}
}
buildManager.update(build);
buildSubmitted(build);
} catch (ValidationException e) {

View File

@ -820,7 +820,7 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
objectIdCache.put(revision, optional);
}
if (mustExist && !optional.isPresent())
throw new ObjectNotFoundException("Unable to find object '" + revision + "'");
throw new ObjectNotFoundException("Unable to find revision '" + revision + "'");
return optional.orNull();
}

View File

@ -106,7 +106,7 @@ public class JobRunResource {
jobRun.getParams(), refName, SecurityUtils.getUser(), request,
null, jobRun.getReason());
if (build.isFinished())
jobManager.resubmit(build, "Rebuild via restful api");
jobManager.resubmit(build, jobRun.getReason());
return build.getId();
}

View File

@ -25,6 +25,7 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotAcceptableException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@ -108,6 +109,20 @@ public class ProjectResource {
throw new UnauthorizedException();
return ProjectData.from(project);
}
@Api(order=125)
@Path("/ids/{path:.*}")
@GET
public Long getProjectId(@PathParam("path") String path) {
var project = projectManager.findByPath(path);
if (project != null) {
if (!SecurityUtils.canAccessProject(project))
throw new NotFoundException("Project not found or inaccessible: " + path);
return project.getId();
} else {
throw new NotFoundException("Project not found or inaccessible: " + path);
}
}
@Api(order=150)
@Path("/{projectId}/clone-url")

View File

@ -7,7 +7,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
@ -109,8 +108,6 @@ public abstract class NewIssueEditor extends FormComponentPanel<Issue> implement
Class<?> fieldBeanClass = FieldUtils.getFieldBeanClass();
Serializable fieldBean = issue.getFieldBean(fieldBeanClass, true);
System.out.println(FieldUtils.getFieldValues(null, fieldBean, Set.of("Assignees")));
var fieldNames = getIssueSetting().getPromptFieldsUponIssueOpen(getProject());
issue.setFieldValues(FieldUtils.getFieldValues(new ComponentContext(this), fieldBean,
FieldUtils.getEditableFields(getProject(), fieldNames)));

View File

@ -58,7 +58,7 @@ public class RawBlobResource extends AbstractResource {
String projectPath = params.get(ProjectMapperUtils.PARAM_PROJECT).toString();
Project project = getProjectManager().findByPath(projectPath);
if (project == null)
throw new EntityNotFoundException();
throw new EntityNotFoundException("Project not found: " + projectPath);
List<String> revisionAndPathSegments = new ArrayList<>();
for (int i = 0; i < params.getIndexedCount(); i++) {