private static String taskLinkFor(WorkflowRun build) { String taskLink = "job/" + Name.of(build).replace("/", "/job/"); taskLink += "/" + build.getNumber() + "/"; return taskLink; }
@Override public void triggerManual(String projectName, String upstreamName, String buildId) throws AuthenticationException { LOG.fine("Manual/Input step called for project: " + projectName + " and build id: " + buildId); WorkflowJob workflowJob; try { workflowJob = ProjectUtil.getWorkflowJob(projectName, getOwnerItemGroup()); RunList<WorkflowRun> builds = workflowJob.getBuilds(); for (WorkflowRun run : builds) { if (Integer.toString(run.getNumber()).equals(buildId)) { InputAction inputAction = run.getAction(InputAction.class); if (inputAction != null && !inputAction.getExecutions().isEmpty()) { inputAction.getExecutions().get(0).doProceedEmpty(); } } } } catch (IOException | PipelineException e) { LOG.warning("Failed to resolve project to trigger manual/input: " + e); } }
@Override public void abortBuild(String projectName, String buildId) throws TriggerException { try { WorkflowJob workflowJob = ProjectUtil.getWorkflowJob(projectName, getOwnerItemGroup()); if (!workflowJob.hasAbortPermission()) { throw new BadCredentialsException("Not authorized to abort build"); } RunList<WorkflowRun> builds = workflowJob.getBuilds(); Optional<WorkflowRun> run = builds.stream() .filter(r -> Integer.toString(r.getNumber()).equals(buildId)) .findFirst(); run.ifPresent(WorkflowRun::doStop); } catch (PipelineException e) { throw new TriggerException("Could not abort build"); } }
@Override public void onDeleted(WorkflowRun run) { globalPipelineMavenConfig.getDao().deleteBuild(run.getParent().getFullName(), run.getNumber()); }
@Override public void onCompleted(WorkflowRun workflowRun, @Nonnull TaskListener listener) { super.onCompleted(workflowRun, listener); // Note: run.duration is zero in onCompleted(), do the substraction in this listener Result result = workflowRun.getResult(); if (result == null) { result = Result.SUCCESS; // FIXME more elegant handling } globalPipelineMavenConfig.getDao().updateBuildOnCompletion( workflowRun.getParent().getFullName(), workflowRun.getNumber(), result.ordinal, workflowRun.getStartTimeInMillis(), Math.max(System.currentTimeMillis() - workflowRun.getStartTimeInMillis(), 0)); // @see HUDSON-5844 } }
private static Task createStageTask(WorkflowRun build, FlowNode stageStartNode, Status stageStatus) { return new Task(stageStartNode.getId(), stageStartNode.getDisplayName(), build.getNumber(), stageStatus, taskLinkFor(build), null, null, StatusType.PAUSED_PENDING_INPUT.equals(stageStatus.getType())); }
@Override public void onInitialize(WorkflowRun run) { super.onInitialize(run); for (Cause cause: run.getCauses()) { if (cause instanceof Cause.UpstreamCause) { Cause.UpstreamCause upstreamCause = (Cause.UpstreamCause) cause; String upstreamJobName = upstreamCause.getUpstreamProject(); int upstreamBuildNumber = upstreamCause.getUpstreamBuild(); globalPipelineMavenConfig.getDao().recordBuildUpstreamCause(upstreamJobName, upstreamBuildNumber, run.getParent().getFullName(), run.getNumber()); } } }
private static Task resolveTask(WorkflowRun build, FlowNode stageStartNode, FlowNode taskNode) throws PipelineException { TaskAction action = taskNode.getAction(TaskAction.class); Status status = resolveTaskStatus(build, stageStartNode, taskNode, action); return new Task(taskNode.getId(), action.getTaskName(), build.getNumber(), status, taskLinkFor(build), null, null, StatusType.PAUSED_PENDING_INPUT.equals(status.getType())); }
private static Stage getStage(WorkflowRun build, FlowNode stageStartNode) throws PipelineException { List<Run> runs = workflowApi.getRunsFor(build.getParent()); Run run = getRunById(runs, build.getNumber()); Stage stage = run.getStageByName(stageStartNode.getDisplayName()); if (stage == null) { throw new PipelineException("Could not resolve stage " + stageStartNode.getDisplayName() + " for pipeline " + build.getDisplayName()); } return stage; }
/** Sets up a running build that is waiting on input. */ private static WorkflowRun runBasicPauseOnInput(JenkinsRule j, String jobName, int[] jobIdNumber, FlowDurabilityHint durabilityHint) throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, jobName); job.setDefinition(new CpsFlowDefinition("input 'pause'", true)); job.addProperty(new DurabilityHintJobProperty(durabilityHint)); WorkflowRun run = job.scheduleBuild2(0).getStartCondition().get(); ListenableFuture<FlowExecution> listener = run.getExecutionPromise(); FlowExecution exec = listener.get(); while(exec.getCurrentHeads().isEmpty() || (exec.getCurrentHeads().get(0) instanceof FlowStartNode)) { // Wait until input step starts System.out.println("Waiting for input step to begin"); Thread.sleep(50); } while(run.getAction(InputAction.class) == null) { // Wait until input step starts System.out.println("Waiting for input action to get attached to run"); Thread.sleep(50); } Thread.sleep(100L); // A little extra buffer for persistence etc jobIdNumber[0] = run.getNumber(); return run; }
/** Create and run a basic build before we mangle its persisted contents. Stores job number to jobIdNumber index 0. */ private static WorkflowRun runBasicBuild(JenkinsRule j, String jobName, int[] jobIdNumber, FlowDurabilityHint hint) throws Exception { WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, jobName); job.setDefinition(new CpsFlowDefinition("echo 'doSomething'", true)); job.addProperty(new DurabilityHintJobProperty(hint)); WorkflowRun run = j.buildAndAssertSuccess(job); jobIdNumber[0] = run.getNumber(); assertCompletedCleanly(run); return run; }
@Override public void evaluate() throws Throwable { WorkflowJob p = story.j.jenkins.getItemByFullName("p", WorkflowJob.class); WorkflowRun b3 = p.getLastBuild(); assertEquals(3, b3.getNumber()); // Resume #3, and verify that the build completes with the expected replacements. SemaphoreStep.success("wait/3", null); story.j.waitForCompletion(b3); story.j.assertLogNotContains("trying edits", b3); story.j.assertLogContains("new first part", b3); story.j.assertLogContains("newer second part", b3); } });
@Override public void evaluate() throws Throwable { WorkflowJob p = story.j.jenkins.getItemByFullName("p/dev%2Fmain", WorkflowJob.class); assertNotNull(p); sampleRepo.git("commit", "--all", "--message=Flow"); sampleRepo.notifyCommit(story.j); WorkflowRun b2 = p.getLastBuild(); assertEquals(2, b2.getNumber()); story.j.assertLogContains("Branch=dev/main", b2); story.j.assertLogContains("workspace=dev_main", b2); verifyProject(p); } });
@Override public void evaluate() throws Throwable { SemaphoreStep.success("wait/1", null); WorkflowJob p = story.j.jenkins.getItemByFullName("p/master", WorkflowJob.class); assertNotNull(p); WorkflowRun b1 = p.getLastBuild(); assertNotNull(b1); assertEquals(1, b1.getNumber()); story.j.assertLogContains("initial content", story.j.waitForCompletion(b1)); SCMBinderTest.assertRevisionAction(b1); } });
@Override public void evaluate() throws Throwable { WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "p"); // As in loadStep, will set up a main and auxiliary script. FilePath f = story.j.jenkins.getWorkspaceFor(p).child("f.groovy"); f.write("'original text'", null); p.setDefinition(new CpsFlowDefinition("node {def t = load 'f.groovy'; echo \"got ${t}\"}", true)); WorkflowRun b1 = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0)); story.j.assertLogContains("got original text", b1); // s/got/received/ on main script assertEquals(0, new CLICommandInvoker(story.j, "replay-pipeline").withStdin(IOUtils.toInputStream("node {def t = load 'f.groovy'; echo \"received ${t}\"}")).invokeWithArgs("p").returnCode()); story.j.waitUntilNoActivity(); WorkflowRun b2 = p.getLastBuild(); assertEquals(2, b2.getNumber()); story.j.assertLogContains("received original text", b2); // s/original/new/ on auxiliary script, and explicitly asking to replay #1 rather than the latest assertEquals(0, new CLICommandInvoker(story.j, "replay-pipeline").withStdin(IOUtils.toInputStream("'new text'")).invokeWithArgs("p", "-n", "1", "-s", "Script1").returnCode()); story.j.waitUntilNoActivity(); WorkflowRun b3 = p.getLastBuild(); assertEquals(3, b3.getNumber()); // Main script picked up from #1, not #2. story.j.assertLogContains("got new text", b3); } });
@Test public void deletedJenkinsfile() throws Exception { sampleGitRepo.init(); sampleGitRepo.write("Jenkinsfile", "node { echo 'Hello World' }"); sampleGitRepo.git("add", "Jenkinsfile"); sampleGitRepo.git("commit", "--all", "--message=flow"); WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p"); mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleGitRepo.toString(), "", "*", "", false))); WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master"); assertEquals(1, mp.getItems().size()); r.waitUntilNoActivity(); WorkflowRun b1 = p.getLastBuild(); assertEquals(1, b1.getNumber()); sampleGitRepo.git("rm", "Jenkinsfile"); sampleGitRepo.git("commit", "--all", "--message=remove"); WorkflowRun b2 = r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); r.assertLogContains("Jenkinsfile not found", b2); }
@Issue("JENKINS-32179") @Test public void conflictingBranches() throws Exception { sampleRepo.init(); sampleRepo.write("Jenkinsfile", ""); sampleRepo.git("add", "Jenkinsfile"); sampleRepo.git("commit", "--all", "--message=flow"); WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p"); mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false), new DefaultBranchPropertyStrategy(new BranchProperty[0]))); mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false), new DefaultBranchPropertyStrategy(new BranchProperty[0]))); WorkflowJob p = scheduleAndFindBranchProject(mp, "master"); mp.getIndexing().writeWholeLogTo(System.out); assertEquals(1, mp.getItems().size()); r.waitForCompletion(p.getLastBuild()); Thread.sleep(1000); assert !p.isBuilding(); WorkflowRun b1 = p.getLastBuild(); assertEquals(1, b1.getNumber()); Queue.Item it = mp.scheduleBuild2(0); Thread.sleep(100); if (it != null) { System.out.println("indexing"); } it.getFuture().waitForStart(); it.getFuture().get(); // mp.scheduleBuild2(0).getFuture().get(); // mp.getIndexing().writeWholeLogTo(System.out); // assertEquals("[p, p/master]", ExtensionList.lookup(Listener.class).get(0).names.toString()); } @TestExtension("conflictingBranches") public static class Listener extends ItemListener {
@Test public void multibranch() throws Exception { sampleRepo.init(); sampleRepo.write("Jenkinsfile", "node {checkout scm; echo readFile('file')}"); sampleRepo.write("file", "initial content"); sampleRepo.git("add", "Jenkinsfile"); sampleRepo.git("commit", "--all", "--message=init"); WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p"); mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false), new DefaultBranchPropertyStrategy(new BranchProperty[0]))); WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master"); r.waitUntilNoActivity(); WorkflowRun b1 = p.getLastBuild(); assertNotNull(b1); assertEquals(1, b1.getNumber()); r.assertLogContains("initial content", b1); sampleRepo.write("file", "subsequent content"); sampleRepo.git("add", "file"); sampleRepo.git("commit", "--message=next"); // Replaying main script with some upcasing. WorkflowRun b2 = (WorkflowRun) b1.getAction(ReplayAction.class).run("node {checkout scm; echo readFile('file').toUpperCase()}", Collections.<String,String>emptyMap()).get(); assertEquals(2, b2.number); // For a multibranch project, we expect checkout scm to retrieve the same repository revision as the (original) Jenkinsfile. r.assertLogContains("INITIAL CONTENT", b2); }
@SuppressWarnings("deprecation") // RunList.size @Test public void useBuildDiscarder() throws Exception { sampleRepo.init(); sampleRepo.write("Jenkinsfile", "properties([buildDiscarder(logRotator(numToKeepStr: '1'))])"); sampleRepo.git("add", "Jenkinsfile"); sampleRepo.git("commit", "--all", "--message=flow"); WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p"); mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false))); WorkflowJob p = scheduleAndFindBranchProject(mp, "master"); assertEquals(1, mp.getItems().size()); r.waitUntilNoActivity(); // #1 built automatically assertEquals(1, p.getBuilds().size()); r.assertBuildStatusSuccess(p.scheduleBuild2(0)); // #2 assertEquals(1, p.getBuilds().size()); r.assertBuildStatusSuccess(p.scheduleBuild2(0)); // #3 assertEquals(1, p.getBuilds().size()); WorkflowRun b3 = p.getLastBuild(); assertEquals(3, b3.getNumber()); assertNull(b3.getPreviousBuild()); }
WorkflowRun b1 = p.getLastBuild(); assertNotNull(b1); assertEquals(1, b1.getNumber()); j.assertBuildStatus(Result.FAILURE, b1);