Merge remote-tracking branch 'origin/master' into feature/JENKINS-36211

This commit is contained in:
Thorsten Scherler 2016-07-12 19:10:54 +02:00
commit 2b71744a94
23 changed files with 376 additions and 93 deletions

4
Jenkinsfile vendored
View File

@ -1,11 +1,12 @@
node {
deleteDir()
checkout scm
docker.image('cloudbees/java-build-tools').inside {
withEnv(['GIT_COMMITTER_EMAIL=me@hatescake.com','GIT_COMMITTER_NAME=Hates','GIT_AUTHOR_NAME=Cake','GIT_AUTHOR_EMAIL=hates@cake.com']) {
try {
sh "mvn clean install -B -DcleanNode -Dmaven.test.failure.ignore"
sh "node checkdeps.js"
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
step([$class: 'ArtifactArchiver', artifacts: '*/target/*.hpi'])
} catch(err) {
@ -36,4 +37,3 @@ def sendhipchat() {
hipchatSend message: message, color: color
}
}

View File

@ -12,7 +12,7 @@ import {
connect,
} from '../redux';
const { object, array, func, string, boolean } = PropTypes;
const { object, array, func, string, bool } = PropTypes;
const EmptyState = ({ repoName, pipeline, showRunButton }) => (
<main>
@ -35,7 +35,7 @@ const EmptyState = ({ repoName, pipeline, showRunButton }) => (
EmptyState.propTypes = {
repoName: string,
pipeline: object,
showRunButton: boolean,
showRunButton: bool,
};
const RunNonMultiBranchPipeline = ({ pipeline, buttonText }) => (

View File

@ -24,41 +24,40 @@
<artifactId>mailer</artifactId>
</dependency>
<dependency>
<!-- Deadlock bug fix in WorkflowRun.doStop(). See https://github.com/jenkinsci/workflow-job-plugin/pull/2 -->
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<version>2.2-beta-1</version>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<version>2.1</version>
<version>2.8</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-api</artifactId>
<version>2.0</version>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<version>2.0</version>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-support</artifactId>
<version>2.0</version>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-multibranch</artifactId>
<version>2.0</version>
<version>2.8</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>

View File

@ -2,6 +2,7 @@ package io.jenkins.blueocean.service.embedded.rest;
import hudson.model.Action;
import hudson.model.FreeStyleBuild;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.plugins.git.util.BuildData;
import io.jenkins.blueocean.commons.ServiceException;
@ -9,10 +10,12 @@ import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BluePipelineNodeContainer;
import io.jenkins.blueocean.rest.model.BluePipelineStepContainer;
import io.jenkins.blueocean.rest.model.BlueQueueItem;
import io.jenkins.blueocean.rest.model.BlueRun;
import io.jenkins.blueocean.rest.model.Container;
import io.jenkins.blueocean.rest.model.Containers;
import io.jenkins.blueocean.rest.model.GenericResource;
import org.jenkinsci.plugins.workflow.cps.replay.ReplayAction;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.export.Exported;
@ -128,6 +131,24 @@ public class AbstractRunImpl<T extends Run> extends BlueRun {
return new LogResource(run.getLogText());
}
@Override
public BlueQueueItem replay() {
ReplayAction replayAction = run.getAction(ReplayAction.class);
if(replayAction == null) {
throw new ServiceException.BadRequestExpception("This run does not support replay");
}
Queue.Item item = replayAction.run2(replayAction.getOriginalScript(), replayAction.getOriginalLoadedScripts());
BlueQueueItem queueItem = QueueContainerImpl.getQueuedItem(item, run.getParent());
if(queueItem == null) {
throw new ServiceException.UnexpectedErrorException("Run was not added to queue.");
} else {
return queueItem;
}
}
@Override
public Container<BlueArtifact> getArtifacts() {
Map<String, BlueArtifact> m = new HashMap<String, BlueArtifact>();

View File

@ -77,4 +77,23 @@ public abstract class BluePipelineFactory implements ExtensionPoint {
}
return i == null ? target : i;
}
/**
* Gives {@link BluePipeline} instance from the first pipeline found.
*
* @param item {@link Item} for which corresponding BlueOcean API object needs to be found
* @param parent Parent {@link Reachable} object
* @return {@link BluePipeline} if a map of item to BlueOcean API found, null otherwise.
*
*/
public static BluePipeline getPipelineInstance(Item item, final Reachable parent){
for(BluePipelineFactory factory:BluePipelineFactory.all()){
BluePipeline pipeline = factory.getPipeline(item, parent);
if(pipeline != null){
return pipeline;
}
}
return null;
}
}

View File

@ -26,7 +26,8 @@ public class FavoriteContainerImpl extends BlueFavoriteContainer {
}
@Override
public BlueFavorite get(final String name) {
public BlueFavorite get(String name) {
name = FavoriteUtil.decodeFullName(name);
if(user.isFavorite(name)){
Item item = Jenkins.getInstance().getItemByFullName(name);
if(FavoriteUtil.isFavorableItem(item)) {

View File

@ -1,13 +1,12 @@
package io.jenkins.blueocean.service.embedded.rest;
import hudson.Util;
import hudson.model.Item;
import hudson.model.Job;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.hal.LinkResolver;
import io.jenkins.blueocean.rest.model.BlueFavorite;
import jenkins.branch.MultiBranchProject;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import jenkins.model.Jenkins;
import io.jenkins.blueocean.service.embedded.util.FavoriteUtil;
/**
* @author Vivek Pandey
@ -17,26 +16,24 @@ public class FavoriteImpl extends BlueFavorite {
private final Object item;
private final Link self;
public FavoriteImpl(Object item, Link self) {
this.self = self;
this.item = item;
}
public FavoriteImpl(Item item, Reachable parent) {
this.self = parent.getLink().rel(Util.rawEncode(item.getFullName()));
this.self = parent.getLink().rel(FavoriteUtil.encodeFullName(item.getFullName()));
LinkResolver linkResolver = Jenkins.getInstance().getInjector().getInstance(LinkResolver.class);
final Link link = linkResolver.resolve(item);
this.item = BluePipelineFactory.getPipelineInstance(item, new Reachable() {
@Override
public Link getLink() {
return link.ancestor();
}
});
// TODO: MBP nested inside folder won't work nor would a favorited folder is going to work
//TODO: Needs https://issues.jenkins-ci.org/browse/JENKINS-36286 to be fixed,
// it should use LinkResolver for it to work in all cases
Object obj = null;
if(item instanceof WorkflowJob){
if(item.getParent() instanceof MultiBranchProject){
Link s = OrganizationImpl.INSTANCE.getLink().rel(String.format("pipelines/%s/branches/",
((MultiBranchProject) item.getParent()).getName()));
obj = new BranchImpl((Job) item, s);
}
}
if(obj == null){
this.item = new PipelineImpl((Job) item);
}else{
this.item = obj;
}
}
@Override

View File

@ -10,6 +10,7 @@ import io.jenkins.blueocean.rest.Navigable;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BlueFavorite;
import io.jenkins.blueocean.rest.model.BlueFavoriteAction;
import io.jenkins.blueocean.rest.model.BlueMultiBranchPipeline;
import io.jenkins.blueocean.rest.model.BluePipeline;
@ -52,7 +53,7 @@ public class MultiBranchPipelineImpl extends BlueMultiBranchPipeline {
@Override
public void favorite(@JsonBody BlueFavoriteAction favoriteAction) {
public BlueFavorite favorite(@JsonBody BlueFavoriteAction favoriteAction) {
if(favoriteAction == null) {
throw new ServiceException.BadRequestExpception("Must provide pipeline name");
}
@ -62,7 +63,8 @@ public class MultiBranchPipelineImpl extends BlueMultiBranchPipeline {
throw new ServiceException.BadRequestExpception("no master branch to favorite");
}
FavoriteUtil.favoriteJob(job.getFullName(), favoriteAction.isFavorite());
Link link = FavoriteUtil.favoriteJob(job.getFullName(), favoriteAction.isFavorite());
return new FavoriteImpl(new BranchImpl(job, getLink().rel("branches")), link);
}
@Override

View File

@ -39,14 +39,12 @@ public class MultiBranchPipelineQueueContainer extends BlueQueueContainer {
if(item.task instanceof ExecutorStepExecution.PlaceholderTask) {
ExecutorStepExecution.PlaceholderTask task = (ExecutorStepExecution.PlaceholderTask) item.task;
int runNumber;
if(task.run() == null){
runNumber = pipeline.job.getNextBuildNumber();
return QueueContainerImpl.getQueuedItem(item, pipeline.job);
}else{
runNumber = task.run().getNumber();
return new QueueItemImpl(item, item.task.getOwnerTask().getName(), task.run().getNumber(),
self.rel(String.valueOf(item.getId())));
}
return new QueueItemImpl(item, item.task.getOwnerTask().getName(), runNumber,
self.rel(String.valueOf(item.getId())));
}
}

View File

@ -7,6 +7,7 @@ import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BlueFavorite;
import io.jenkins.blueocean.rest.model.BlueFavoriteAction;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineContainer;
@ -87,12 +88,13 @@ public class PipelineFolderImpl extends BluePipelineFolder {
@Override
public void favorite(@JsonBody BlueFavoriteAction favoriteAction) {
public BlueFavorite favorite(@JsonBody BlueFavoriteAction favoriteAction) {
if(favoriteAction == null) {
throw new ServiceException.BadRequestExpception("Must provide pipeline name");
}
FavoriteUtil.favoriteJob(folder.getFullName(), favoriteAction.isFavorite());
Link link = FavoriteUtil.favoriteJob(folder.getFullName(), favoriteAction.isFavorite());
return new FavoriteImpl(this, link);
}
@Override

View File

@ -9,6 +9,7 @@ import io.jenkins.blueocean.rest.Navigable;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BlueFavorite;
import io.jenkins.blueocean.rest.model.BlueFavoriteAction;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BlueQueueContainer;
@ -103,12 +104,13 @@ public class PipelineImpl extends BluePipeline {
@Override
public void favorite(@JsonBody BlueFavoriteAction favoriteAction) {
public BlueFavorite favorite(@JsonBody BlueFavoriteAction favoriteAction) {
if(favoriteAction == null) {
throw new ServiceException.BadRequestExpception("Must provide pipeline name");
}
FavoriteUtil.favoriteJob(job.getFullName(), favoriteAction.isFavorite());
Link link = FavoriteUtil.favoriteJob(job.getFullName(), favoriteAction.isFavorite());
return new FavoriteImpl(this, link);
}
@Override

View File

@ -1,5 +1,6 @@
package io.jenkins.blueocean.service.embedded.rest;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import hudson.model.BuildableItem;
import hudson.model.Job;
@ -10,7 +11,9 @@ import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BlueQueueContainer;
import io.jenkins.blueocean.rest.model.BlueQueueItem;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.github.util.FluentIterableWrapper;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.List;
@ -28,7 +31,7 @@ public class QueueContainerImpl extends BlueQueueContainer {
@Override
public BlueQueueItem get(String name) {
for (BlueQueueItem blueQueueItem : getQueuedItems(job, pipeline)) {
for (BlueQueueItem blueQueueItem : getQueuedItems(job)) {
if(name.equals(blueQueueItem.getId())){
return blueQueueItem;
}
@ -39,7 +42,7 @@ public class QueueContainerImpl extends BlueQueueContainer {
@Override
public Iterator<BlueQueueItem> iterator() {
return getQueuedItems(job, pipeline).iterator();
return getQueuedItems(job).iterator();
}
/**
@ -50,7 +53,9 @@ public class QueueContainerImpl extends BlueQueueContainer {
*
* @return List of items newest first
*/
public static List<BlueQueueItem> getQueuedItems(Job job, BluePipeline pipeline) {
public static List<BlueQueueItem> getQueuedItems(Job job) {
BluePipeline pipeline = new PipelineImpl(job);
if(job instanceof BuildableItem) {
BuildableItem task = (BuildableItem)job;
List<Queue.Item> items = Jenkins.getInstance().getQueue().getItems(task);
@ -68,6 +73,15 @@ public class QueueContainerImpl extends BlueQueueContainer {
}
}
public static BlueQueueItem getQueuedItem(final Queue.Item item, Job job) {
return FluentIterableWrapper.from(QueueContainerImpl.getQueuedItems(job))
.firstMatch(new Predicate<BlueQueueItem>() {
@Override
public boolean apply(@Nullable BlueQueueItem input) {
return input.getId().equalsIgnoreCase(Long.toString(item.getId()));
}
}).orNull();
}
@Override
public Link getLink() {
return pipeline.getLink().rel("queue");

View File

@ -88,13 +88,7 @@ public class RunContainerImpl extends BlueRunContainer {
if(scheduleResult.isAccepted()) {
final Queue.Item item = scheduleResult.getItem();
BlueQueueItem queueItem = FluentIterableWrapper.from(QueueContainerImpl.getQueuedItems(job, pipeline))
.firstMatch(new Predicate<BlueQueueItem>() {
@Override
public boolean apply(@Nullable BlueQueueItem input) {
return input.getId().equalsIgnoreCase(Long.toString(item.getId()));
}
}).orNull();
BlueQueueItem queueItem = QueueContainerImpl.getQueuedItem(item, job);
if (queueItem == null) {
throw new ServiceException.UnexpectedErrorException("The queue item does not exist in the queue");

View File

@ -1,5 +1,6 @@
package io.jenkins.blueocean.service.embedded.util;
import hudson.Util;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Job;
@ -7,21 +8,23 @@ import hudson.model.User;
import hudson.plugins.favorite.FavoritePlugin;
import hudson.plugins.favorite.user.FavoriteUserProperty;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.service.embedded.rest.UserImpl;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
import org.kohsuke.stapler.Stapler;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
/**
* @author Ivan Meredith
*/
public class FavoriteUtil {
public static void favoriteJob(String fullName, boolean favorite) {
public static Link favoriteJob(String fullName, boolean favorite) {
User user = User.current();
if(user == null) {
throw new ServiceException.ForbiddenException("Must be logged in to use set favotites");
throw new ServiceException.ForbiddenException("Must be logged in to use set favorites");
}
boolean set = false;
FavoriteUserProperty fup = user.getProperty(FavoriteUserProperty.class);
@ -31,7 +34,7 @@ public class FavoriteUtil {
//TODO: FavoritePlugin is null
FavoritePlugin plugin = Jenkins.getInstance().getPlugin(FavoritePlugin.class);
if(plugin == null) {
throw new ServiceException.UnexpectedErrorException("Can not find instance of favorites plugin");
throw new ServiceException.UnexpectedErrorException("Can not find instance of Favorite Plugin");
}
if(favorite != set) {
try {
@ -40,25 +43,23 @@ public class FavoriteUtil {
throw new ServiceException.UnexpectedErrorException("Something went wrong setting the favorite", e);
}
}
}
public static String generateBlueUrl(String org, Item i) {
String url = "/organizations/" + org + "/pipelines/";
if(i instanceof WorkflowJob) {
WorkflowJob job = (WorkflowJob)i;
ItemGroup it = job.getParent();
if(it instanceof WorkflowMultiBranchProject) {
url += ((WorkflowMultiBranchProject) it).getName() + "/branches/" + job.getName();
} else {
url += job.getName();
}
} else {
url += i.getName();
}
return url;
return new UserImpl(user).getLink().rel("favorites/"+ Util.rawEncode(FavoriteUtil.encodeFullName(fullName)));
}
public static boolean isFavorableItem(Item i){
return i!= null && (i instanceof Job || i instanceof ItemGroup);
}
public static String encodeFullName(String name){
return Util.rawEncode(Util.rawEncode(name));
}
public static String decodeFullName(String name){
try {
return URLDecoder.decode(URLDecoder.decode(name, "UTF-8"), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new ServiceException.UnexpectedErrorException("Something went wrong URL decoding fullName: "+name, e);
}
}
}

View File

@ -0,0 +1,118 @@
package io.jenkins.blueocean.service.embedded;
import hudson.model.Label;
import hudson.model.Queue;
import io.jenkins.blueocean.service.embedded.rest.PipelineImpl;
import io.jenkins.blueocean.service.embedded.rest.PipelineRunImpl;
import io.jenkins.blueocean.service.embedded.scm.GitSampleRepoRule;
import jenkins.branch.BranchProperty;
import jenkins.branch.BranchSource;
import jenkins.branch.DefaultBranchPropertyStrategy;
import jenkins.plugins.git.GitSCMSource;
import jenkins.scm.api.SCMSource;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.cps.replay.ReplayAction;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import java.util.Map;
import static org.junit.Assert.assertEquals;
/**
* @author Ivan Meredith
*/
public class AbstractRunImplTest extends BaseTest {
@Rule
public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
@Before
public void setup() throws Exception{
super.setup();
sampleRepo.init();
}
//Disabled, see JENKINS-36453
//@Test
public void replayRunTest() throws Exception {
WorkflowJob job1 = j.jenkins.createProject(WorkflowJob.class, "pipeline1");
j.createOnlineSlave(Label.get("remote"));
job1.setDefinition(new CpsFlowDefinition(
"node('remote') {\n" +
" ws {\n" +
" git($/" + sampleRepo + "/$)\n" +
" }\n" +
"}"));
WorkflowRun b1 = job1.scheduleBuild2(0).get();
j.assertBuildStatusSuccess(b1);
sampleRepo.write("file1", "");
sampleRepo.git("add", "file1");
sampleRepo.git("commit", "--message=init");
WorkflowRun b2 = job1.scheduleBuild2(0).get();
j.assertBuildStatusSuccess(b2);
Assert.assertNotEquals(new PipelineRunImpl(b1, null).getCommitId(), new PipelineRunImpl(b2, null).getCommitId());
request().post("/organizations/jenkins/pipelines/pipeline1/runs/1/replay").build(String.class);
j.waitForCompletion(job1.getLastBuild());
Map r = request().get("/organizations/jenkins/pipelines/pipeline1/runs/3/").build(Map.class);
Assert.assertEquals(r.get("commitId"), new PipelineRunImpl(b2,null).getCommitId());
}
@Test
public void replayRunTestMB() throws Exception {
j.createOnlineSlave(Label.get("remote"));
sampleRepo.write("Jenkinsfile", "node('remote') {\n" +
" ws {\n" +
" checkout scm\n" +
" stage 'build'\n "+"node {echo 'Building'}\n"+
" stage 'test'\nnode { echo 'Testing'}\n"+
" stage 'deploy'\nnode { echo 'Deploying'}\n" +
" }\n" +
" }");
sampleRepo.git("add", "Jenkinsfile");
sampleRepo.git("commit", "--message=init");
WorkflowMultiBranchProject mp = j.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false),
new DefaultBranchPropertyStrategy(new BranchProperty[0])));
for (SCMSource source : mp.getSCMSources()) {
assertEquals(mp, source.getOwner());
}
mp.scheduleBuild2(0).getFuture().get();
WorkflowJob job1 = mp.getItem("master");
WorkflowRun b1 = job1.scheduleBuild2(0).waitForStart();
j.waitForCompletion(b1);
j.assertBuildStatusSuccess(b1);
sampleRepo.write("file1", "");
sampleRepo.git("add", "file1");
sampleRepo.git("commit", "--message=init");
WorkflowRun b2 = job1.scheduleBuild2(0).get();
j.assertBuildStatusSuccess(b2);
Assert.assertNotEquals(new PipelineRunImpl(b1, null).getCommitId(), new PipelineRunImpl(b2, null).getCommitId());
Map replayBuild = request().post("/organizations/jenkins/pipelines/p/branches/master/runs/"+ b1.getNumber()+"/replay").build(Map.class);
Queue.Item item = j.getInstance().getQueue().getItem(Long.parseLong((String)replayBuild.get("id")));
WorkflowRun replayedRun = (WorkflowRun)item.getFuture().get();
Map r = request().get("/organizations/jenkins/pipelines/p/branches/master/runs/"+replayedRun.getNumber()+"/").build(Map.class);
Assert.assertEquals(new PipelineRunImpl(b1,null).getCommitId(), r.get("commitId"));
}
}

View File

@ -428,11 +428,13 @@ public class MultiBranchTest extends BaseTest{
WorkflowJob p = scheduleAndFindBranchProject(mp, "master");
j.waitUntilNoActivity();
new RequestBuilder(baseUrl)
Map m = new RequestBuilder(baseUrl)
.put("/organizations/jenkins/pipelines/p/favorite")
.auth("alice", "alice")
.data(ImmutableMap.of("favorite", true))
.build(String.class);
.build(Map.class);
validatePipeline(p, (Map) m.get("item"));
List l = new RequestBuilder(baseUrl)
.get("/users/"+user.getId()+"/favorites/")
@ -467,11 +469,16 @@ public class MultiBranchTest extends BaseTest{
WorkflowJob p = scheduleAndFindBranchProject(mp, "master");
j.waitUntilNoActivity();
new RequestBuilder(baseUrl)
WorkflowJob p1 = scheduleAndFindBranchProject(mp, "feature2");
Map map = new RequestBuilder(baseUrl)
.put("/organizations/jenkins/pipelines/p/branches/feature2/favorite")
.auth("alice", "alice")
.data(ImmutableMap.of("favorite", true))
.build(String.class);
.build(Map.class);
validatePipeline(p1, (Map) map.get("item"));
List l = new RequestBuilder(baseUrl)
.get("/users/"+user.getId()+"/favorites/")
@ -480,7 +487,6 @@ public class MultiBranchTest extends BaseTest{
Assert.assertEquals(l.size(), 1);
WorkflowJob p1 = scheduleAndFindBranchProject(mp, "feature2");
Map branch = (Map)((Map)l.get(0)).get("item");
validatePipeline(p1, branch);

View File

@ -2,7 +2,6 @@ package io.jenkins.blueocean.service.embedded;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import hudson.model.Project;
import hudson.model.User;
import hudson.tasks.Mailer;
@ -93,12 +92,13 @@ public class ProfileApiTest extends BaseTest{
user.setFullName("Alice Cooper");
Project p = j.createFreeStyleProject("pipeline1");
new RequestBuilder(baseUrl)
Map map = new RequestBuilder(baseUrl)
.put("/organizations/jenkins/pipelines/pipeline1/favorite")
.auth("alice", "alice")
.data(ImmutableMap.of("favorite", true))
.build(String.class);
.build(Map.class);
validatePipeline(p, (Map) map.get("item"));
List l = new RequestBuilder(baseUrl)
.get("/users/"+user.getId()+"/favorites/")
.auth("alice","alice")

View File

@ -349,6 +349,24 @@ Pipelines can be nested inside folder.
"qeueudTime" : "2016-06-22T11:05:41.309+1200"
}
## Replay a pipeline run
This will quueue up a replay of the pipeline run with the same commit id as the run used
curl -XPOST http://localhost:8080/jenkins/blue/rest/organizations/jenkins/pipelines/pipeline3/runs/1/replay
{
"_class" : "io.jenkins.blueocean.service.embedded.rest.QueueItemImpl",
"id" : "64",
"expectedBuildNumber" : 10,
"pipeline" : "bug%2FUX-334",
"_links" : {
"self" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/bo2/queue/64/"
}
},
"queuedTime" : "2016-06-29T14:11:52.191-0700"
}
## Get all runs in a pipeline
curl -v -X GET http://localhost:8080/jenkins/blue/rest/organizations/jenkins/pipelines/pipeline1/runs
@ -1101,19 +1119,68 @@ This will show up as a download in the browser.
Unit testing...
## Favorite API
Favorite API can be used to favorite a pipeline (Multi-branch, branch, pipeline or even folder) for a logged in user.
If favorite request is successful then the repsonse is favorited item.
curl -u alice:xxx -H"Content-Type:application/json" -XPUT -d '{"favorite":true} ttp://localhost:56748/jenkins/blue/rest/organizations/jenkins/pipelines/pipeline1/favorite
{
"_class" : "io.jenkins.blueocean.service.embedded.rest.FavoriteImpl",
"_links" : {
"self" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/users/alice/favorites/pipeline1/"
}
},
"item" : {
"displayName" : "pipeline1",
"_links" : {
"runs" : {
"href" : "/blue/rest/organizations/jenkins/pipelines/pipeline1/runs/",
"_class" : "io.jenkins.blueocean.rest.hal.Link"
},
"self" : {
"href" : "/blue/rest/organizations/jenkins/pipelines/pipeline1/",
"_class" : "io.jenkins.blueocean.rest.hal.Link"
},
"queue" : {
"href" : "/blue/rest/organizations/jenkins/pipelines/pipeline1/queue/",
"_class" : "io.jenkins.blueocean.rest.hal.Link"
},
"actions" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/pipeline1/actions/"
}
},
"organization" : "jenkins",
"latestRun" : null,
"name" : "pipeline1",
"actions" : [],
"weatherScore" : 100,
"_class" : "io.jenkins.blueocean.service.embedded.rest.PipelineImpl",
"fullName" : "pipeline1",
"lastSuccessfulRun" : null,
"estimatedDurationInMillis" : -1
}
}
## Favorite a pipeline
Returns 200 on success. Must be authenticated.
curl -u bob:bob -H"Content-Type:application/json" -XPUT -d '{"favorite":true} ttp://localhost:56748/jenkins/blue/rest/organizations/jenkins/pipelines/pipeline1/favorite
## Favorite a multibranch pipeline
## Favorite a multi branch pipeline
Must be authenticated.
This favorites the master branch. Returns 200 on success. 500 if master does not exist
curl -u bob:bob -H"Content-Type:application/json" -XPUT -d '{"favorite":true} http://localhost:56748/jenkins/blue/rest/organizations/jenkins/pipelines/pipeline1/favorite
## Favorite a multibranch pipeline branch
## Favorite a multi branch pipeline branch
Returns 200 on success. Must be authenticated.
curl -H"Content-Type:application/json" -XPUT -d '{"favorite":true} http://localhost:56748/jenkins/blue/rest/organizations/jenkins/pipelines/pipeline1/branches/master/favorite

View File

@ -15,6 +15,7 @@ public final class Link {
public Link(String href) {
this.href = Links.ensureTrailingSlash(href);
assert this.href.endsWith("/");
}
@Exported(name = "href")
@ -28,4 +29,27 @@ public final class Link {
public Link rel(String name) {
return new Link(href+name);
}
/**
* Gives ancestor Link, for example, for link("/a/b/c/d"), ancestor is "a/b/c/".
*/
public Link ancestor(){
int i = href.lastIndexOf("/");
if(i>0) {
int j = href.substring(0, i).lastIndexOf("/");
if (j > 0) {
return new Link(href.substring(0, j));
}
}
return new Link("/");
}
@Override
public String toString() {
return href;
}
}

View File

@ -1,5 +1,6 @@
package io.jenkins.blueocean.rest.model;
import io.jenkins.blueocean.commons.stapler.TreeResponse;
import io.jenkins.blueocean.rest.Navigable;
import io.jenkins.blueocean.rest.annotation.Capability;
import org.kohsuke.stapler.WebMethod;
@ -96,5 +97,6 @@ public abstract class BluePipeline extends Resource {
@PUT
@WebMethod(name="favorite")
public abstract void favorite(@JsonBody BlueFavoriteAction favoriteAction);
@TreeResponse
public abstract BlueFavorite favorite(@JsonBody BlueFavoriteAction favoriteAction);
}

View File

@ -7,6 +7,7 @@ import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.json.JsonResponse;
import org.kohsuke.stapler.verb.POST;
import org.kohsuke.stapler.verb.PUT;
import java.text.SimpleDateFormat;
@ -193,6 +194,14 @@ public abstract class BlueRun extends Resource {
@Navigable
public abstract Object getLog();
/**
* Replays a pipeline. The SCM commit/revision used in the existing and new runs should match.
*
* @return The queued item.
*/
@POST @TreeResponse @WebMethod(name = "replay")
public abstract BlueQueueItem replay();
public enum BlueRunState {
QUEUED,
RUNNING,

View File

@ -33,7 +33,7 @@
"keymirror": "0.1.1",
"moment": "2.13.0",
"react": "15.1.0",
"react-addons-css-transition-group": "15.0.1",
"react-addons-css-transition-group": "15.1.0",
"react-dom": "15.1.0",
"react-redux": "4.4.5",
"react-router": "2.3.0",

View File

@ -3,14 +3,16 @@
/*********************************************************************************************
**********************************************************************************************
Checks for version inconsistencies in top-level project dependencies.
Checks for version inconsistencies in PROD dependencies for top-level
projects, and a few first-party "blessed" deps like JDL etc.
Usage:
node checkdeps.js
If conflicting versions are detected, this will print them out as JSON on STDERR and
exit(1). If no conflicts are detected, there is no output and normal exit
Any conflicting PROD dependencies will be printed on STDERR, and it will exit(1)
If no conflicts, or only PEER/DEV conflicts, normal exit(0)
**********************************************************************************************
*********************************************************************************************/
@ -40,11 +42,16 @@ var packageFiles = [];
packageFiles.push(require("./blueocean-dashboard/package.json"));
packageFiles.push(require("./blueocean-web/package.json"));
// Add some expected dependencies, so we go another level deep just for these
packageFiles.push(require("./blueocean-dashboard/node_modules/@jenkins-cd/design-language/package.json"));
packageFiles.push(require("./blueocean-dashboard/node_modules/@jenkins-cd/sse-gateway/package.json"));
packageFiles.push(require("./blueocean-dashboard/node_modules/@jenkins-cd/js-extensions/package.json"));
packageFiles.forEach(packageFile => {
addDependencies("prod", packageFile.dependencies);
addDependencies("dev", packageFile.devDependencies);
addDependencies("peer", packageFile.peerDependencies);
// addDependencies("dev", packageFile.devDependencies);
// addDependencies("peer", packageFile.peerDependencies);
function addDependencies(kind, deps) {
if (deps) {
@ -73,5 +80,5 @@ Object.keys(allDependencies).forEach(dependency => {
if (errs.length) {
console.error(JSON.stringify(errs, null, 4));
process.exit(1);
process.exitCode = 1;
}