26 | This setting allows users to change the default durability mode for running Pipelines. In most cases this is a
27 | trade-off between performance and the ability for running pipelines to resume after unplanned Jenkins outages.
28 |
29 |
What does this do?
30 |
31 |
Previously, running pipelines wrote data constantly, so that they could resume even if Jenkins fails.
32 | This setting gives the user the ability to adjust this write behavior.
33 |
Higher-performance/lower-durability modes write data to disk much less often for running pipelines.
34 |
Writing data less often can massively reduce build times for Pipelines with many steps or complex logic.
35 | For pipelines which spend most of their time waiting for a shell/batch script to run, the difference will be less visible.
36 |
Running pipelines with lower durability settings may lose data if they do not finish and Jenkins is not shut down gracefully:
37 |
38 |
A "graceful" shutdown is where Jenkins goes through a full shutdown process, such as visiting http://[jenkins-server]/exit
39 |
A "dirty" shutdown, such as using kill -9 to terminate the Jenkins process, may prevent incomplete pipelines from persisting data
40 |
41 |
Pipelines that cannot persist data may not be able to resume or displayed in Blue Ocean/Stage View/etc.
42 |
Pipelines will generally write log data regardless of durability settings.
43 |
Some modes use an "atomic write" option - this helps ensure that pipeline build files aren't overwritten or left partially written if something fails.
44 |
Atomic writes may place more stress on filesystems, so especially with networked storage it may be faster not to use them.
45 |
46 |
47 |
48 | Note: defaults may be set globally under Manage Jenkins > Configure System.
49 |
2 | Updates the properties of the job which runs this step.
3 | Mainly useful from multibranch Pipelines, so that Jenkinsfile itself can encode what would otherwise be static job configuration.
4 | Existing properties set through the Jenkins UI for non-multibranch Pipelines will be preserved.
5 |
6 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/Messages.properties:
--------------------------------------------------------------------------------
1 | ReadTrustedStep._has_been_modified_in_an_untrusted_revis=\u2018{0}\u2019 has been modified in an untrusted revision
2 | WorkflowMultiBranchProject.DisplayName=Multibranch Pipeline
3 | WorkflowMultiBranchProject.Description=Creates a set of Pipeline projects according to detected branches in one SCM repository.
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/Messages_fr.properties:
--------------------------------------------------------------------------------
1 | ReadTrustedStep._has_been_modified_in_an_untrusted_revis=\u2018{0}\u2019 a \u00e9t\u00e9 modifi\u00e9 dans une r\u00e9vision non fiable
2 | WorkflowMultiBranchProject.DisplayName=Pipeline Multibranches
3 | WorkflowMultiBranchProject.Description=Cr\u00e9e un ensemble de projets Pipeline en se basant sur les branches d\u00e9tect\u00e9es dans le d\u00e9p\u00f4t d'un gestionnaire de code source.
4 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/Messages_zh_CN.properties:
--------------------------------------------------------------------------------
1 | # The MIT License
2 | #
3 | # Copyright (c) 2018, suren
4 | #
5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
6 | # of this software and associated documentation files (the "Software"), to deal
7 | # in the Software without restriction, including without limitation the rights
8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | # copies of the Software, and to permit persons to whom the Software is
10 | # furnished to do so, subject to the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be included in
13 | # all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | # THE SOFTWARE.
22 |
23 | ReadTrustedStep._has_been_modified_in_an_untrusted_revis=\u2018{0}\u2019 \u5df2\u7ecf\u5728\u672a\u6388\u4fe1\u7684\u7248\u672c\u4e2d\u88ab\u4fee\u6539
24 | WorkflowMultiBranchProject.DisplayName=\u591a\u5206\u652f\u6d41\u6c34\u7ebf
25 | WorkflowMultiBranchProject.Description=\u6839\u636e\u4e00\u4e2aSCM\u4ed3\u5e93\u4e2d\u68c0\u6d4b\u5230\u7684\u5206\u652f\u521b\u5efa\u4e00\u7cfb\u5217\u6d41\u6c34\u7ebf\u3002
26 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/ReadTrustedStep/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/ReadTrustedStep/help-path.html:
--------------------------------------------------------------------------------
1 |
2 | Relative (slash-separated) path to the file from the SCM root.
3 | Thus readTrusted 'subdir/file' is similar to node {checkout scm; readFile 'subdir/file'}.
4 |
2 | From a multibranch Pipeline project, reads a file from the associated SCM and returns its contents.
3 | Unlike the readFile step, no workspace is required.
4 | If the associated branch is not trusted, yet the file has been modified from its trusted version, an error is thrown.
5 | Thus this step is useful for loading scripts or other files which might otherwise be used to run malicious commands.
6 | Like checkout scm, as a convenience it may also be used from a standalone project configured with Pipeline from SCM,
7 | in which case there is no security aspect.
8 |
25 | When you have a multi-branch pipeline that checks out other sibling repositories, you may want to check out the
26 | matching branch from that sibling repository (assuming it exists) but fall back to the main branch if there is no
27 | matching branch.
28 |
29 | This step lets you create a branch in the primary repository and then when you need downstream / upstream changes in
30 | the sibling repository you can just create a matching branch and it will be resolved automatically.
31 | For example:
32 |
// checkout the main source
33 | dir('main'){
34 | // this will checkout the source repository that is driving the multi-branch pipeline
35 | checkout scm
36 | }
37 | // now checkout the tests
38 | dir('tests'){
39 | // this will check if there is a branch with the same name as the current branch in
40 | // https://example.com/example.git and use that for the checkout, but if there is no
41 | // branch with the same name it will fall back to the master branch
42 | checkout resolveScm(source: git('https://example.com/example.git'), targets: [BRANCH_NAME,'master']
43 | }
44 | // rest of pipeline
45 |
46 |
47 | The return value is the resolved SCM instance (or null if ignoring errors).
48 | Where the SCM implementation supports it, the SCM instance will be pinned to the current head revision of
49 | the resolved branch. This can be useful if, for example, you want to check out the resolved branch on
50 | multiple nodes because all the nodes will get the same revision.
51 |
52 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/SCMBinder/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/SCMVar/help.jelly:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
28 | Represents the SCM configuration in a multibranch project build.
29 | Use checkout scm to check out sources matching Jenkinsfile.
30 | You may also use this in a standalone project configured with Pipeline from SCM,
31 | though in that case the checkout will just be of the latest revision in the branch,
32 | possibly newer than the revision from which the Pipeline was loaded.
33 |
34 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/WorkflowBranchProjectFactory/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/WorkflowBranchProjectFactory/getting-started-links.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/WorkflowBranchProjectFactory/getting-started.jelly:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 | Pipeline Branch projects support building branches within a repository containing a pipeline script. By default it uses
27 | a file named Jenkinsfile in the root directory.
28 |
29 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/WorkflowBranchProjectFactory/help-scriptPath.html:
--------------------------------------------------------------------------------
1 |
2 | Relative location within the checkout of your Pipeline script.
3 | Note that it will always be run inside a Groovy sandbox.
4 | Default is Jenkinsfile if left empty.
5 | (just use checkout scm to retrieve sources from the same location as is configured here)
6 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/WorkflowMultiBranchProjectFactory/getting-started.jelly:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 | Pipeline Multibranch projects recognize and build repositories with a file named Jenkinsfile in branches of the repository.
27 |
28 |
--------------------------------------------------------------------------------
/src/main/resources/org/jenkinsci/plugins/workflow/multibranch/WorkflowMultiBranchProjectFactory/help-scriptPath.html:
--------------------------------------------------------------------------------
1 |
2 | Relative location within the checkout of your Pipeline script.
3 | Note that it will always be run inside a Groovy sandbox.
4 | Default is Jenkinsfile if left empty.
5 | (just use checkout scm to retrieve sources from the same location as is configured here)
6 |
7 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/DurabilityHintBranchPropertyWorkflowTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.multibranch;
26 |
27 | import hudson.model.Result;
28 | import jenkins.branch.BranchProperty;
29 | import jenkins.branch.BranchSource;
30 | import jenkins.branch.DefaultBranchPropertyStrategy;
31 | import jenkins.branch.NoTriggerBranchProperty;
32 | import jenkins.branch.NoTriggerOrganizationFolderProperty;
33 | import jenkins.plugins.git.GitSCMSource;
34 | import jenkins.plugins.git.GitSampleRepoRule;
35 | import org.jenkinsci.plugins.workflow.flow.DurabilityHintProvider;
36 | import org.jenkinsci.plugins.workflow.flow.FlowDurabilityHint;
37 | import org.jenkinsci.plugins.workflow.flow.GlobalDefaultFlowDurabilityLevel;
38 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
39 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
40 | import org.jenkinsci.plugins.workflow.job.properties.DurabilityHintJobProperty;
41 | import org.junit.Assert;
42 | import org.junit.Rule;
43 | import org.junit.Test;
44 | import org.jvnet.hudson.test.Issue;
45 | import org.jvnet.hudson.test.JenkinsRule;
46 |
47 | import static org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject;
48 |
49 | /**
50 | * Integration test for {@link NoTriggerBranchProperty} and {@link NoTriggerOrganizationFolderProperty}.
51 | */
52 | @Issue("JENKINS-32396")
53 | public class DurabilityHintBranchPropertyWorkflowTest {
54 |
55 | @Rule public JenkinsRule r = new JenkinsRule();
56 | @Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
57 |
58 | @Test
59 | public void configRoundtrip() throws Exception {
60 | WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
61 | BranchSource bs = new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false));
62 | mp.getSourcesList().add(bs);
63 | bs.setStrategy(new DefaultBranchPropertyStrategy(new BranchProperty[]{new DurabilityHintBranchProperty(FlowDurabilityHint.SURVIVABLE_NONATOMIC)}));
64 | r.configRoundtrip(mp);
65 | DefaultBranchPropertyStrategy strat = (DefaultBranchPropertyStrategy) mp.getBranchPropertyStrategy(mp.getSCMSources().get(0));
66 | DurabilityHintBranchProperty prop = null;
67 | for (BranchProperty bp : strat.getProps()) {
68 | if (bp instanceof DurabilityHintBranchProperty) {
69 | prop = (DurabilityHintBranchProperty)bp;
70 | break;
71 | }
72 | }
73 | Assert.assertNotNull(prop);
74 | Assert.assertEquals(FlowDurabilityHint.SURVIVABLE_NONATOMIC, prop.getHint());
75 | }
76 |
77 | @Test public void durabilityHintByPropertyStep() throws Exception {
78 | sampleRepo.init();
79 | sampleRepo.write("Jenkinsfile",
80 | "properties([durabilityHint('" + FlowDurabilityHint.SURVIVABLE_NONATOMIC.getName()+"')])\n"+
81 | "echo 'whynot'");
82 | sampleRepo.git("add", "Jenkinsfile");
83 | sampleRepo.git("commit", "--all", "--message=flow");
84 |
85 |
86 | WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
87 | mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false)));
88 | WorkflowJob p = scheduleAndFindBranchProject(mp, "master");
89 | r.waitUntilNoActivity();
90 |
91 | WorkflowRun b1 = p.getLastBuild();
92 | Assert.assertEquals(Result.SUCCESS, b1.getResult());
93 | DurabilityHintJobProperty prop = p.getProperty(DurabilityHintJobProperty.class);
94 | Assert.assertEquals(FlowDurabilityHint.SURVIVABLE_NONATOMIC, prop.getHint());
95 | }
96 |
97 | @Test
98 | @Issue("JENKINS-48826")
99 | public void durabilityHintByBranchProperty() throws Exception {
100 | sampleRepo.init();
101 | sampleRepo.write("Jenkinsfile",
102 | "echo 'whynot'");
103 | sampleRepo.git("add", "Jenkinsfile");
104 | sampleRepo.git("commit", "--all", "--message=flow");
105 |
106 |
107 | WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
108 | BranchSource bs = new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false));
109 | mp.getSourcesList().add(bs);
110 | bs.setStrategy(new DefaultBranchPropertyStrategy(new BranchProperty[]{new DurabilityHintBranchProperty(FlowDurabilityHint.SURVIVABLE_NONATOMIC)}));
111 | WorkflowJob p = scheduleAndFindBranchProject(mp, "master");
112 | r.waitUntilNoActivity();
113 |
114 | Assert.assertEquals(FlowDurabilityHint.SURVIVABLE_NONATOMIC, DurabilityHintProvider.suggestedFor(p));
115 | WorkflowRun b1 = p.getLastBuild();
116 | Assert.assertEquals(Result.SUCCESS, b1.getResult());
117 |
118 | // Ensure when we remove the property, branches see that on the next build
119 | bs.setStrategy(new DefaultBranchPropertyStrategy(new BranchProperty[]{}));
120 | p = scheduleAndFindBranchProject(mp, "master");
121 | r.waitUntilNoActivity();
122 |
123 | Assert.assertEquals(GlobalDefaultFlowDurabilityLevel.getDefaultDurabilityHint(), DurabilityHintProvider.suggestedFor(mp.getItems().iterator().next()));
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/GitDirectorySCMNavigator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2015 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.multibranch;
26 |
27 | import edu.umd.cs.findbugs.annotations.NonNull;
28 | import hudson.Extension;
29 | import hudson.Launcher;
30 | import hudson.Proc;
31 | import hudson.model.TaskListener;
32 | import java.io.ByteArrayInputStream;
33 | import java.io.ByteArrayOutputStream;
34 | import java.io.File;
35 | import java.io.IOException;
36 | import java.util.regex.Matcher;
37 | import java.util.regex.Pattern;
38 | import jenkins.plugins.git.GitSCMSource;
39 | import jenkins.scm.api.SCMNavigator;
40 | import jenkins.scm.api.SCMNavigatorDescriptor;
41 | import jenkins.scm.api.SCMSourceObserver;
42 | import org.apache.commons.io.IOUtils;
43 | import static org.junit.Assert.assertFalse;
44 |
45 | import org.jenkinsci.Symbol;
46 | import org.kohsuke.stapler.DataBoundConstructor;
47 |
48 | /**
49 | * Sample provider which scans a directory for Git checkouts.
50 | */
51 | public class GitDirectorySCMNavigator extends SCMNavigator {
52 |
53 | private final String directory;
54 |
55 | @DataBoundConstructor public GitDirectorySCMNavigator(String directory) {
56 | this.directory = directory;
57 | }
58 |
59 | public String getDirectory() {
60 | return directory;
61 | }
62 |
63 | @NonNull
64 | @Override
65 | protected String id() {
66 | return directory;
67 | }
68 |
69 | @Override public void visitSources(SCMSourceObserver observer) throws IOException, InterruptedException {
70 | TaskListener listener = observer.getListener();
71 | File[] kids = new File(directory).listFiles();
72 | if (kids == null) {
73 | listener.error(directory + " does not exist");
74 | return;
75 | }
76 | for (File kid : kids) {
77 | if (!observer.isObserving()) {
78 | return;
79 | }
80 | if (!new File(kid, ".git").isDirectory()) {
81 | listener.getLogger().format("Ignoring %s since it does not look like a Git checkout%n", kid);
82 | continue;
83 | }
84 | listener.getLogger().format("Considering: %s%n", kid);
85 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
86 | Proc proc = new Launcher.LocalLauncher(listener).launch().pwd(kid).cmds("git", "remote", "-v").stdout(baos).start();
87 | if (proc.join() != 0) {
88 | listener.error("git-remote failed");
89 | continue;
90 | }
91 | String origin = kid.getAbsolutePath(); // fallback
92 | for (String line : IOUtils.readLines(new ByteArrayInputStream(baos.toByteArray()))) {
93 | Matcher m = ORIGIN.matcher(line);
94 | if (m.matches()) {
95 | origin = m.group(1);
96 | listener.getLogger().format("Found origin URL: %s%n", origin);
97 | assertFalse(origin.startsWith("git@"));
98 | break;
99 | }
100 | }
101 | SCMSourceObserver.ProjectObserver projectObserver = observer.observe(kid.getName());
102 | projectObserver.addSource(new GitSCMSource(getId() + "::" + kid.getName(), origin, "", "*", "", false));
103 | projectObserver.complete();
104 | }
105 | }
106 | private static final Pattern ORIGIN = Pattern.compile("origin\t(.+) [(]fetch[)]");
107 |
108 | @Extension @Symbol("gitDirectory")
109 | public static class DescriptorImpl extends SCMNavigatorDescriptor {
110 |
111 | @NonNull
112 | @Override public String getDisplayName() {
113 | return "Directory of Git checkouts";
114 | }
115 |
116 | @Override public SCMNavigator newInstance(String name) {
117 | return null;
118 | }
119 |
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/NoTriggerBranchPropertyWorkflowTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.multibranch;
26 |
27 | import hudson.model.queue.QueueTaskFuture;
28 | import java.util.Collections;
29 | import jenkins.branch.BranchProperty;
30 | import jenkins.branch.BranchSource;
31 | import jenkins.branch.NamedExceptionsBranchPropertyStrategy;
32 | import jenkins.branch.NoTriggerBranchProperty;
33 | import jenkins.branch.NoTriggerOrganizationFolderProperty;
34 | import jenkins.branch.OrganizationFolder;
35 | import jenkins.plugins.git.GitSCMSource;
36 | import jenkins.plugins.git.GitSampleRepoRule;
37 | import jenkins.scm.impl.SingleSCMNavigator;
38 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
39 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
40 | import org.junit.Test;
41 | import static org.junit.Assert.*;
42 | import org.junit.Rule;
43 | import org.jvnet.hudson.test.Issue;
44 | import org.jvnet.hudson.test.JenkinsRule;
45 |
46 | /**
47 | * Integration test for {@link NoTriggerBranchProperty} and {@link NoTriggerOrganizationFolderProperty}.
48 | */
49 | @Issue("JENKINS-32396")
50 | public class NoTriggerBranchPropertyWorkflowTest {
51 |
52 | @Rule public JenkinsRule r = new JenkinsRule();
53 | @Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
54 |
55 | @Issue("JENKINS-30206")
56 | @Test public void singleRepo() throws Exception {
57 | round1();
58 | WorkflowMultiBranchProject p = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
59 | BranchSource branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo.toString(), "", "*", "", false));
60 | branchSource.setStrategy(new NamedExceptionsBranchPropertyStrategy(new BranchProperty[0], new NamedExceptionsBranchPropertyStrategy.Named[] {
61 | new NamedExceptionsBranchPropertyStrategy.Named("release*", new BranchProperty[] {new NoTriggerBranchProperty()})
62 | }));
63 | p.getSourcesList().add(branchSource);
64 | // Should be initial builds of master & newfeature but not release.
65 | WorkflowJob master = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(p, "master");
66 | r.waitUntilNoActivity();
67 | assertEquals(2, master.getNextBuildNumber());
68 | WorkflowJob release = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(p, "release");
69 | assertEquals(1, release.getNextBuildNumber());
70 | WorkflowJob newfeature = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(p, "newfeature");
71 | assertEquals(2, newfeature.getNextBuildNumber());
72 | round2();
73 | WorkflowMultiBranchProjectTest.showIndexing(p);
74 | // Should be second builds of master & newfeature but not release.
75 | assertEquals(3, master.getNextBuildNumber());
76 | assertEquals(1, release.getNextBuildNumber());
77 | assertEquals(3, newfeature.getNextBuildNumber());
78 | // Should be able to manually build release.
79 | QueueTaskFuture releaseBuild = release.scheduleBuild2(0);
80 | assertNotNull(releaseBuild);
81 | assertEquals(1, releaseBuild.get().getNumber());
82 | assertEquals(2, release.getNextBuildNumber());
83 | // Updating configuration should take effect for next time: new builds of newfeature & release but not master.
84 | branchSource = new BranchSource(new GitSCMSource("source-id", sampleRepo.toString(), "", "*", "", false));
85 | branchSource.setStrategy(new NamedExceptionsBranchPropertyStrategy(new BranchProperty[0], new NamedExceptionsBranchPropertyStrategy.Named[] {
86 | new NamedExceptionsBranchPropertyStrategy.Named("master", new BranchProperty[] {new NoTriggerBranchProperty()})
87 | }));
88 | p.getSourcesList().clear();
89 | p.getSourcesList().add(branchSource);
90 | round3();
91 | WorkflowMultiBranchProjectTest.showIndexing(p);
92 | assertEquals(3, master.getNextBuildNumber());
93 | assertEquals(3, release.getNextBuildNumber());
94 | assertEquals(4, newfeature.getNextBuildNumber());
95 | }
96 |
97 | @Test public void organizationFolder() throws Exception {
98 | round1();
99 | OrganizationFolder top = r.jenkins.createProject(OrganizationFolder.class, "top");
100 | top.getProperties().add(new NoTriggerOrganizationFolderProperty("(?!release.*).*"));
101 | top.getNavigators().add(new SingleSCMNavigator("p", Collections.singletonList(new GitSCMSource("source-id", sampleRepo.toString(), "", "*", "", false))));
102 | top.scheduleBuild2(0).getFuture().get();
103 | r.waitUntilNoActivity();
104 | top.getComputation().writeWholeLogTo(System.out);
105 | WorkflowMultiBranchProject p = r.jenkins.getItemByFullName("top/p", WorkflowMultiBranchProject.class);
106 | assertNotNull(p);
107 | WorkflowJob master = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(p, "master");
108 | r.waitUntilNoActivity();
109 | assertEquals(2, master.getNextBuildNumber());
110 | WorkflowJob release = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(p, "release");
111 | assertEquals(1, release.getNextBuildNumber());
112 | WorkflowJob newfeature = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(p, "newfeature");
113 | assertEquals(2, newfeature.getNextBuildNumber());
114 | round2();
115 | WorkflowMultiBranchProjectTest.showIndexing(p);
116 | assertEquals(3, master.getNextBuildNumber());
117 | assertEquals(1, release.getNextBuildNumber());
118 | assertEquals(3, newfeature.getNextBuildNumber());
119 | QueueTaskFuture releaseBuild = release.scheduleBuild2(0);
120 | assertNotNull(releaseBuild);
121 | assertEquals(1, releaseBuild.get().getNumber());
122 | assertEquals(2, release.getNextBuildNumber());
123 | top.getProperties().replace(new NoTriggerOrganizationFolderProperty("(?!master$).*"));
124 | round3();
125 | WorkflowMultiBranchProjectTest.showIndexing(p);
126 | assertEquals(3, master.getNextBuildNumber());
127 | assertEquals(3, release.getNextBuildNumber());
128 | assertEquals(4, newfeature.getNextBuildNumber());
129 | }
130 |
131 | private void round1() throws Exception {
132 | sampleRepo.init();
133 | sampleRepo.write("Jenkinsfile", "");
134 | sampleRepo.git("add", "Jenkinsfile");
135 | sampleRepo.git("commit", "--message=init");
136 | sampleRepo.git("checkout", "-b", "newfeature");
137 | sampleRepo.write("Jenkinsfile", "// newfeature");
138 | sampleRepo.git("commit", "--all", "--message=newfeature");
139 | sampleRepo.git("checkout", "-b", "release", "master");
140 | sampleRepo.write("Jenkinsfile", "// release");
141 | sampleRepo.git("commit", "--all", "--message=release");
142 | }
143 |
144 | private void round2() throws Exception {
145 | sampleRepo.git("checkout", "master");
146 | sampleRepo.write("Jenkinsfile", "// more");
147 | sampleRepo.git("commit", "--all", "--message=master-2");
148 | sampleRepo.git("checkout", "newfeature");
149 | sampleRepo.write("Jenkinsfile", "// more");
150 | sampleRepo.git("commit", "--all", "--message=newfeature-2");
151 | sampleRepo.git("checkout", "release");
152 | sampleRepo.write("Jenkinsfile", "// more");
153 | sampleRepo.git("commit", "--all", "--message=release-2");
154 | sampleRepo.notifyCommit(r);
155 | }
156 |
157 | private void round3() throws Exception {
158 | sampleRepo.git("checkout", "master");
159 | sampleRepo.write("Jenkinsfile", "// yet more");
160 | sampleRepo.git("commit", "--all", "--message=master-3");
161 | sampleRepo.git("checkout", "newfeature");
162 | sampleRepo.write("Jenkinsfile", "// yet more");
163 | sampleRepo.git("commit", "--all", "--message=newfeature-3");
164 | sampleRepo.git("checkout", "release");
165 | sampleRepo.write("Jenkinsfile", "// yet more");
166 | sampleRepo.git("commit", "--all", "--message=release-3");
167 | sampleRepo.notifyCommit(r);
168 | }
169 |
170 | }
171 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/RepairBranchPropertyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2019 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 | package org.jenkinsci.plugins.workflow.multibranch;
25 |
26 | import com.cloudbees.hudson.plugins.folder.computed.FolderComputation;
27 | import hudson.model.Actionable;
28 | import hudson.model.Cause;
29 | import hudson.model.Job;
30 | import hudson.model.Queue;
31 | import hudson.model.Result;
32 | import hudson.model.TopLevelItem;
33 | import jenkins.branch.MultiBranchProject;
34 | import jenkins.branch.OrganizationFolder;
35 | import jenkins.scm.api.SCMRevisionAction;
36 | import jenkins.scm.impl.mock.MockSCMController;
37 | import jenkins.scm.impl.mock.MockSCMDiscoverBranches;
38 | import jenkins.scm.impl.mock.MockSCMNavigator;
39 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
40 | import org.junit.Before;
41 | import org.junit.Rule;
42 | import org.junit.Test;
43 | import org.jvnet.hudson.test.Issue;
44 | import org.jvnet.hudson.test.JenkinsRule;
45 | import org.jvnet.hudson.test.recipes.LocalData;
46 |
47 | import java.io.IOException;
48 |
49 | import static junit.framework.TestCase.assertEquals;
50 | import static junit.framework.TestCase.assertNotNull;
51 | import static junit.framework.TestCase.assertNull;
52 | import static junit.framework.TestCase.assertTrue;
53 |
54 | public class RepairBranchPropertyTest {
55 |
56 | @Rule
57 | public JenkinsRule j = new JenkinsRule();
58 | private MockSCMController controller;
59 |
60 | @Before
61 | public void setUp() throws IOException {
62 | setup(MockSCMController.create());
63 | }
64 |
65 | void setup(MockSCMController co) throws IOException {
66 | controller = co;
67 | controller.createRepository("repo");
68 | controller.createBranch("repo", "master");
69 | controller.addFile("repo", "master", "First!", WorkflowBranchProjectFactory.SCRIPT,
70 | "echo 'hello'".getBytes());
71 | }
72 |
73 | @Test @Issue("JENKINS-55116")
74 | public void removedProperty() throws Exception {
75 | OrganizationFolder org = j.createProject(OrganizationFolder.class, "org");
76 | org.getNavigators().add(new MockSCMNavigator(controller.getId(), new MockSCMDiscoverBranches()));
77 | org.save();
78 | org.scheduleBuild(new Cause.UserIdCause("anonymous"));
79 | j.waitUntilNoActivity();
80 | MultiBranchProject, ?> repo = org.getItem("repo");
81 | assertNotNull(repo);
82 | Job, ?> master = repo.getItem("master");
83 | assertNotNull(master);
84 | assertNotNull(master.getProperty(BranchJobProperty.class));
85 | assertNotNull(master.getLastBuild());
86 | master.removeProperty(BranchJobProperty.class);
87 | //removeProperty calls save
88 | j.jenkins.reload();
89 |
90 | org = j.jenkins.getItem("org", j.jenkins, OrganizationFolder.class);
91 | assertNotNull(org);
92 | repo = org.getItem("repo");
93 | assertNotNull(repo);
94 | master = repo.getItem("master");
95 | assertNotNull(master);
96 |
97 | assertNotNull(master.getProperty(BranchJobProperty.class));
98 | assertTrue(repo.getProjectFactory().isProject(master));
99 | assertTrue(repo.getPrimaryView().contains((TopLevelItem)master));
100 | }
101 |
102 | @Test @Issue("JENKINS-55116")
103 | public void removedPropertyLastBuildCorrupt() throws Exception {
104 | OrganizationFolder org = j.createProject(OrganizationFolder.class, "org");
105 | org.getNavigators().add(new MockSCMNavigator(controller.getId(), new MockSCMDiscoverBranches()));
106 | org.save();
107 | org.scheduleBuild(new Cause.UserIdCause("anonymous"));
108 | j.waitUntilNoActivity();
109 | MultiBranchProject, ?> repo = org.getItem("repo");
110 | assertNotNull(repo);
111 | Job, ?> master = repo.getItem("master");
112 | assertNotNull(master);
113 | assertNotNull(master.getProperty(BranchJobProperty.class));
114 | assertNotNull(master.getLastBuild());
115 |
116 | controller.addFile("repo", "master", "Second", "README.txt", "Hello".getBytes());
117 | repo.scheduleBuild();
118 | j.waitUntilNoActivity();
119 | assertEquals(2, master.getBuilds().size());
120 | final Actionable lastBuild = master.getLastBuild();
121 | lastBuild.removeAction(lastBuild.getAction(SCMRevisionAction.class));
122 | assertNull(lastBuild.getAction(SCMRevisionAction.class));
123 |
124 | master.removeProperty(BranchJobProperty.class);
125 | //removeProperty calls save
126 | j.jenkins.reload();
127 | org = j.jenkins.getItem("org", j.jenkins, OrganizationFolder.class);
128 | assertNotNull(org);
129 | repo = org.getItem("repo");
130 | assertNotNull(repo);
131 | master = repo.getItem("master");
132 | assertNotNull(master);
133 |
134 | assertNotNull(master.getProperty(BranchJobProperty.class));
135 | assertTrue(repo.getProjectFactory().isProject(master));
136 | assertTrue(repo.getPrimaryView().contains((TopLevelItem)master));
137 | }
138 |
139 | @Test @LocalData @Issue("JENKINS-55116")
140 | public void removedPropertyAtStartup() throws Exception {
141 | MockSCMController cont = MockSCMController.recreate("9ea2ef21-aa07-4973-a942-6c4c4c7851d1");
142 | setup(cont);
143 | OrganizationFolder org = j.jenkins.getItem("org", j.jenkins, OrganizationFolder.class);
144 | assertNotNull(org);
145 | MultiBranchProject, ?> repo = org.getItem("repo");
146 | assertNotNull(repo);
147 | WorkflowJob master = (WorkflowJob)repo.getItem("master");
148 | assertNotNull(master);
149 | assertNotNull(master.getProperty(BranchJobProperty.class));
150 | assertTrue(((WorkflowMultiBranchProject)master.getParent()).getProjectFactory().isProject(master));
151 | assertTrue(repo.getPrimaryView().contains(master));
152 |
153 | //Can it be scanned successfully afterwards
154 | final Queue.Item item = repo.scheduleBuild2(0);
155 | assertNotNull(item);
156 | final Queue.Executable executable = item.getFuture().get();
157 | assertNotNull(executable);
158 | FolderComputation> computation = (FolderComputation>) executable;
159 | computation.writeWholeLogTo(System.out);
160 | assertEquals(Result.SUCCESS, computation.getResult());
161 | //Since the new controller has new "commits" a build of master should have been scheduled
162 | j.waitUntilNoActivity();
163 | assertEquals(2, master.getBuilds().size());
164 | j.assertBuildStatusSuccess(master.getBuildByNumber(2));
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/ReplayActionTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2016 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.multibranch;
26 |
27 | import hudson.model.Item;
28 | import hudson.model.User;
29 | import hudson.security.ACL;
30 | import hudson.security.ACLContext;
31 | import java.io.File;
32 | import java.util.Collections;
33 | import jenkins.branch.BranchProperty;
34 | import jenkins.branch.BranchSource;
35 | import jenkins.branch.DefaultBranchPropertyStrategy;
36 | import jenkins.branch.MultiBranchProject;
37 | import jenkins.branch.OrganizationFolder;
38 | import jenkins.model.Jenkins;
39 | import jenkins.plugins.git.GitSCMSource;
40 | import jenkins.plugins.git.GitSampleRepoRule;
41 | import jenkins.plugins.git.GitStep;
42 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
43 | import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition;
44 | import org.jenkinsci.plugins.workflow.cps.replay.ReplayAction;
45 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
46 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
47 | import org.junit.Test;
48 | import static org.junit.Assert.*;
49 | import org.junit.ClassRule;
50 | import org.junit.Rule;
51 | import org.junit.rules.TemporaryFolder;
52 | import org.jvnet.hudson.test.BuildWatcher;
53 | import org.jvnet.hudson.test.JenkinsRule;
54 | import org.jvnet.hudson.test.MockAuthorizationStrategy;
55 |
56 | public class ReplayActionTest {
57 |
58 | @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
59 | @Rule public JenkinsRule r = new JenkinsRule();
60 | @Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
61 | @Rule public TemporaryFolder tmp = new TemporaryFolder();
62 |
63 | @Test public void scriptFromSCM() throws Exception {
64 | sampleRepo.init();
65 | sampleRepo.write("Jenkinsfile", "node {checkout scm; echo \"loaded ${readFile 'file'}\"}");
66 | sampleRepo.git("add", "Jenkinsfile");
67 | sampleRepo.write("file", "initial content");
68 | sampleRepo.git("add", "file");
69 | sampleRepo.git("commit", "--message=init");
70 | WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
71 | p.setDefinition(new CpsScmFlowDefinition(new GitStep(sampleRepo.toString()).createSCM(), "Jenkinsfile"));
72 | WorkflowRun b = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
73 | r.assertLogContains("loaded initial content", b);
74 | // Changing contents of a file in the repo. Since this is a standalone project, scm currently “floats” to the branch head.
75 | sampleRepo.write("file", "subsequent content");
76 | sampleRepo.git("add", "file");
77 | sampleRepo.git("commit", "--message=next");
78 | // Replaying with a modified main script; checkout scm will get branch head.
79 | b = (WorkflowRun) b.getAction(ReplayAction.class).run("node {checkout scm; echo \"this time loaded ${readFile 'file'}\"}", Collections.emptyMap()).get();
80 | assertEquals(2, b.number);
81 | r.assertLogContains("this time loaded subsequent content", b);
82 | }
83 |
84 | @Test public void multibranch() throws Exception {
85 | sampleRepo.init();
86 | sampleRepo.write("Jenkinsfile", "node {checkout scm; echo readFile('file')}");
87 | sampleRepo.write("file", "initial content");
88 | sampleRepo.git("add", "Jenkinsfile");
89 | sampleRepo.git("commit", "--all", "--message=init");
90 | WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
91 | mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false), new DefaultBranchPropertyStrategy(new BranchProperty[0])));
92 | WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master");
93 | r.waitUntilNoActivity();
94 | WorkflowRun b1 = p.getLastBuild();
95 | assertNotNull(b1);
96 | assertEquals(1, b1.getNumber());
97 | r.assertLogContains("initial content", b1);
98 | sampleRepo.write("file", "subsequent content");
99 | sampleRepo.git("add", "file");
100 | sampleRepo.git("commit", "--message=next");
101 | // Replaying main script with some upcasing.
102 | WorkflowRun b2 = (WorkflowRun) b1.getAction(ReplayAction.class).run("node {checkout scm; echo readFile('file').toUpperCase()}", Collections.emptyMap()).get();
103 | assertEquals(2, b2.number);
104 | // For a multibranch project, we expect checkout scm to retrieve the same repository revision as the (original) Jenkinsfile.
105 | r.assertLogContains("INITIAL CONTENT", b2);
106 | }
107 |
108 | @Test public void permissions() throws Exception {
109 | File clones = tmp.newFolder();
110 | sampleRepo.init();
111 | sampleRepo.write("Jenkinsfile", "");
112 | sampleRepo.git("add", "Jenkinsfile");
113 | sampleRepo.git("commit", "--all", "--message=init");
114 | sampleRepo.git("clone", ".", new File(clones, "one").getAbsolutePath());
115 | // Set up a secured instance with an organization folder.
116 | // Developers have varying permissions set at the topmost (configurable) level.
117 | r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
118 | OrganizationFolder top = r.jenkins.createProject(OrganizationFolder.class, "top");
119 | r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().
120 | grant(Jenkins.ADMINISTER).everywhere().to("admin").
121 | grant(Jenkins.READ).everywhere().to("dev1", "dev2", "dev3").
122 | grant(Item.CONFIGURE).onFolders(top).to("dev1"). // implies REPLAY
123 | grant(ReplayAction.REPLAY).onFolders(top).to("dev2").
124 | grant(Item.BUILD).onFolders(top).to("dev3")); // does not imply REPLAY
125 | top.getNavigators().add(new GitDirectorySCMNavigator(clones.getAbsolutePath()));
126 | top.scheduleBuild2(0).getFuture().get();
127 | top.getComputation().writeWholeLogTo(System.out);
128 | assertEquals(1, top.getItems().size());
129 | MultiBranchProject,?> one = top.getItem("one");
130 | r.waitUntilNoActivity();
131 | WorkflowJob p = WorkflowMultiBranchProjectTest.findBranchProject((WorkflowMultiBranchProject) one, "master");
132 | WorkflowRun b1 = p.getLastBuild();
133 | assertEquals(1, b1.getNumber());
134 | // Multibranch projects are always sandboxed, so any dev with REPLAY (or CONFIGURE) can replay.
135 | assertTrue(canReplay(b1, "admin"));
136 | // Note that while dev1 cannot actually configure the WorkflowJob (it is read-only; no one can),
137 | // the implication CONFIGURE → REPLAY is done at a lower level than this suppression of CONFIGURE.
138 | assertTrue(canReplay(b1, "dev1"));
139 | assertTrue(canReplay(b1, "dev2"));
140 | assertFalse(canReplay(b1, "dev3"));
141 | // For whole-script-approval standalone projects, you need RUN_SCRIPTS to replay.
142 | p = r.jenkins.createProject(WorkflowJob.class, "p");
143 | p.setDefinition(new CpsFlowDefinition("", false));
144 | b1 = p.scheduleBuild2(0).get();
145 | assertTrue(canReplay(b1, "admin"));
146 | assertFalse("not sandboxed, so only safe for admins", canReplay(b1, "dev1"));
147 | assertFalse(canReplay(b1, "dev2"));
148 | assertFalse(canReplay(b1, "dev3"));
149 | }
150 | private static boolean canReplay(WorkflowRun b, String user) {
151 | final ReplayAction a = b.getAction(ReplayAction.class);
152 | try (ACLContext context = ACL.as(User.getById(user, true))) {
153 | return a.isEnabled();
154 | }
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/ResolveScmStepTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright (c) 2017 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | *
24 | */
25 |
26 | package org.jenkinsci.plugins.workflow.multibranch;
27 |
28 | import hudson.model.TopLevelItem;
29 | import jenkins.scm.impl.mock.MockSCMController;
30 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
31 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
32 | import org.junit.Before;
33 | import org.junit.ClassRule;
34 | import org.junit.Test;
35 | import org.jvnet.hudson.test.JenkinsRule;
36 |
37 | public class ResolveScmStepTest {
38 |
39 | @ClassRule
40 | public static JenkinsRule j = new JenkinsRule();
41 |
42 | @Before
43 | public void cleanOutAllItems() throws Exception {
44 | for (TopLevelItem i : j.getInstance().getItems()) {
45 | i.delete();
46 | }
47 | }
48 |
49 | @Test
50 | public void given_existingHeadName_when_invoked_then_existingHeadNameReturned() throws Exception {
51 | try (MockSCMController c = MockSCMController.create()) {
52 | c.createRepository("repo");
53 | c.createBranch("repo", "foo");
54 | c.addFile("repo", "foo", "Add file", "new-file.txt", "content".getBytes());
55 | WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "workflow");
56 | job.setDefinition(new CpsFlowDefinition("node {\n"
57 | + " def tests = resolveScm source: mockScm(controllerId:'"
58 | + c.getId()
59 | + "', repository:'repo', traits: [discoverBranches()]), "
60 | + "targets:['foo']\n"
61 | + " checkout tests\n"
62 | + " if (!fileExists('new-file.txt')) { error 'wrong branch checked out' }\n"
63 | + "}", true));
64 | j.buildAndAssertSuccess(job);
65 | }
66 | }
67 |
68 | @Test
69 | public void given_nonExistingHeadName_when_invokedIgnoringErrors_then_nullReturned() throws Exception {
70 | try (MockSCMController c = MockSCMController.create()) {
71 | c.createRepository("repo");
72 | c.createBranch("repo", "foo");
73 | WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "workflow");
74 | job.setDefinition(new CpsFlowDefinition("node {\n"
75 | + " def tests = resolveScm source: mockScm(controllerId:'"
76 | + c.getId()
77 | + "', repository:'repo', traits: [discoverBranches()]), "
78 | + "targets:['bar'], ignoreErrors: true\n"
79 | + " if (tests != null) { error \"resolved as ${tests}\"}\n"
80 | + "}", true));
81 | j.buildAndAssertSuccess(job);
82 | }
83 | }
84 |
85 | @Test
86 | public void given_nonExistingHeadName_when_invoked_then_abortThrown() throws Exception {
87 | try (MockSCMController c = MockSCMController.create()) {
88 | c.createRepository("repo");
89 | c.createBranch("repo", "foo");
90 | WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "workflow");
91 | job.setDefinition(new CpsFlowDefinition("node {\n"
92 | + " def ok = true\n"
93 | + " try {\n"
94 | + " def tests = resolveScm source: mockScm(controllerId:'"
95 | + c.getId()
96 | + "', repository:'repo', traits: [discoverBranches()]), "
97 | + "targets:['bar']\n"
98 | + " ok = false\n"
99 | + " } catch (e) {}\n"
100 | + " if (!ok) { error 'abort not thrown' }\n"
101 | + "}", true));
102 | j.buildAndAssertSuccess(job);
103 | }
104 | }
105 |
106 | @Test
107 | public void given_nonExistingHeadName_when_invokedWithDefault_then_defaultReturned() throws Exception {
108 | try (MockSCMController c = MockSCMController.create()) {
109 | c.createRepository("repo");
110 | c.createBranch("repo", "manchu");
111 | c.addFile("repo", "manchu", "Add file", "new-file.txt", "content".getBytes());
112 | WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "workflow");
113 | job.setDefinition(new CpsFlowDefinition("node {\n"
114 | + " def tests = resolveScm source: mockScm(controllerId:'"
115 | + c.getId()
116 | + "', repository:'repo', traits: [discoverBranches()]), "
117 | + "targets:['bar', 'manchu']\n"
118 | + " checkout tests\n"
119 | + " if (!fileExists('new-file.txt')) { error 'wrong branch checked out' }\n"
120 | + "}", true));
121 | j.buildAndAssertSuccess(job);
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/SCMBinderTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2015 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.multibranch;
26 |
27 | import com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy;
28 | import edu.umd.cs.findbugs.annotations.NonNull;
29 | import hudson.Util;
30 | import hudson.model.Item;
31 | import hudson.model.Result;
32 | import hudson.model.TaskListener;
33 | import hudson.model.User;
34 | import hudson.plugins.git.util.BuildData;
35 | import hudson.scm.ChangeLogSet;
36 | import java.io.File;
37 | import java.io.IOException;
38 | import java.util.HashSet;
39 | import java.util.Iterator;
40 | import java.util.List;
41 | import java.util.Set;
42 | import java.util.TreeSet;
43 | import jenkins.branch.BranchSource;
44 | import jenkins.model.Jenkins;
45 | import jenkins.plugins.git.GitBranchSCMRevision;
46 | import jenkins.plugins.git.GitSCMSource;
47 | import jenkins.plugins.git.GitSampleRepoRule;
48 | import jenkins.scm.api.SCMHead;
49 | import jenkins.scm.api.SCMRevision;
50 | import jenkins.scm.api.SCMRevisionAction;
51 | import jenkins.scm.api.SCMSourceDescriptor;
52 | import static org.hamcrest.Matchers.*;
53 |
54 | import org.springframework.security.core.Authentication;
55 | import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
56 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
57 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
58 | import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
59 | import org.junit.Test;
60 | import static org.junit.Assert.*;
61 | import org.junit.ClassRule;
62 | import org.junit.Rule;
63 | import org.jvnet.hudson.test.BuildWatcher;
64 | import org.jvnet.hudson.test.Issue;
65 | import org.jvnet.hudson.test.JenkinsRule;
66 |
67 | public class SCMBinderTest {
68 |
69 | @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
70 | @Rule public JenkinsRule r = new JenkinsRule();
71 | @Rule public GitSampleRepoRule sampleGitRepo = new GitSampleRepoRule();
72 |
73 | @Test public void exactRevisionGit() throws Exception {
74 | sampleGitRepo.init();
75 | ScriptApproval sa = ScriptApproval.get();
76 | sa.approveSignature("staticField hudson.model.Items XSTREAM2");
77 | sa.approveSignature("method com.thoughtworks.xstream.XStream toXML java.lang.Object");
78 | sampleGitRepo.write("Jenkinsfile", "echo hudson.model.Items.XSTREAM2.toXML(scm); semaphore 'wait'; node {checkout scm; echo readFile('file')}");
79 | sampleGitRepo.write("file", "initial content");
80 | sampleGitRepo.git("add", "Jenkinsfile");
81 | sampleGitRepo.git("commit", "--all", "--message=flow");
82 | WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
83 | mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleGitRepo.toString(), "", "*", "", false)));
84 | WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master");
85 | SemaphoreStep.waitForStart("wait/1", null);
86 | WorkflowRun b1 = p.getLastBuild();
87 | assertNotNull(b1);
88 | assertEquals(1, b1.getNumber());
89 | assertRevisionAction(b1);
90 | r.assertLogContains("Obtained Jenkinsfile from ", b1);
91 | sampleGitRepo.write("Jenkinsfile", "node {checkout scm; echo readFile('file').toUpperCase()}");
92 | sampleGitRepo.write("file", "subsequent content");
93 | sampleGitRepo.git("commit", "--all", "--message=tweaked");
94 | SemaphoreStep.success("wait/1", null);
95 | sampleGitRepo.notifyCommit(r);
96 | WorkflowRun b2 = p.getLastBuild();
97 | assertEquals(2, b2.getNumber());
98 | r.assertLogContains("initial content", r.waitForCompletion(b1));
99 | r.assertLogContains("SUBSEQUENT CONTENT", b2);
100 | assertRevisionAction(b2);
101 | WorkflowMultiBranchProjectTest.showIndexing(mp);
102 | List> changeSets = b2.getChangeSets();
103 | assertEquals(1, changeSets.size());
104 | ChangeLogSet extends ChangeLogSet.Entry> changeSet = changeSets.get(0);
105 | assertEquals(b2, changeSet.getRun());
106 | assertEquals("git", changeSet.getKind());
107 | Iterator extends ChangeLogSet.Entry> iterator = changeSet.iterator();
108 | assertTrue(iterator.hasNext());
109 | ChangeLogSet.Entry entry = iterator.next();
110 | assertEquals("tweaked", entry.getMsg());
111 | assertEquals("[Jenkinsfile, file]", new TreeSet<>(entry.getAffectedPaths()).toString());
112 | assertFalse(iterator.hasNext());
113 | }
114 |
115 | public static void assertRevisionAction(WorkflowRun build) {
116 | SCMRevisionAction revisionAction = build.getAction(SCMRevisionAction.class);
117 | assertNotNull(revisionAction);
118 | SCMRevision revision = revisionAction.getRevision();
119 | assertEquals(GitBranchSCMRevision.class, revision.getClass());
120 | Set expected = new HashSet<>();
121 | List buildDataActions = build.getActions(BuildData.class);
122 | if (!buildDataActions.isEmpty()) { // i.e., we have run at least one checkout step, or done a heavyweight checkout to get a single file
123 | for (BuildData data : buildDataActions) {
124 | expected.add(data.lastBuild.marked.getSha1().getName());
125 | }
126 | assertThat(expected, hasItem(((GitBranchSCMRevision) revision).getHash()));
127 | }
128 | }
129 |
130 | @Test public void deletedJenkinsfile() throws Exception {
131 | sampleGitRepo.init();
132 | sampleGitRepo.write("Jenkinsfile", "node { echo 'Hello World' }");
133 | sampleGitRepo.git("add", "Jenkinsfile");
134 | sampleGitRepo.git("commit", "--all", "--message=flow");
135 | WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
136 | mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleGitRepo.toString(), "", "*", "", false)));
137 | WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master");
138 | assertEquals(1, mp.getItems().size());
139 | r.waitUntilNoActivity();
140 | WorkflowRun b1 = p.getLastBuild();
141 | assertEquals(1, b1.getNumber());
142 | sampleGitRepo.git("rm", "Jenkinsfile");
143 | sampleGitRepo.git("commit", "--all", "--message=remove");
144 | WorkflowRun b2 = r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
145 | r.assertLogContains("Jenkinsfile not found", b2);
146 | }
147 |
148 | @Issue("JENKINS-40521")
149 | @Test public void deletedBranch() throws Exception {
150 | r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
151 | sampleGitRepo.init();
152 | // TODO GitSCMSource offers no way to set a GitSCMExtension such as CleanBeforeCheckout; work around with deleteDir
153 | // (without cleaning, b2 will succeed since the workspace will still have a cached origin/feature ref)
154 | sampleGitRepo.write("Jenkinsfile", "node {deleteDir(); checkout scm; echo 'Hello World'}");
155 | sampleGitRepo.git("add", "Jenkinsfile");
156 | sampleGitRepo.git("commit", "--all", "--message=flow");
157 | sampleGitRepo.git("checkout", "-b", "feature");
158 | sampleGitRepo.write("somefile", "stuff");
159 | sampleGitRepo.git("add", "somefile");
160 | sampleGitRepo.git("commit", "--all", "--message=tweaked");
161 | WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
162 | mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleGitRepo.toString(), "", "*", "", false)));
163 | mp.setOrphanedItemStrategy(new DefaultOrphanedItemStrategy(false, "", ""));
164 | WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "feature");
165 | assertEquals(2, mp.getItems().size());
166 | r.waitUntilNoActivity();
167 | WorkflowRun b1 = p.getLastBuild();
168 | assertEquals(1, b1.getNumber());
169 | Authentication auth = User.getById("dev", true).impersonate2();
170 | assertFalse(p.getACL().hasPermission2(auth, Item.DELETE));
171 | assertTrue(p.isBuildable());
172 | sampleGitRepo.git("checkout", "master");
173 | sampleGitRepo.git("branch", "-D", "feature");
174 | { // TODO AbstractGitSCMSource.retrieve(SCMHead, TaskListener) is incorrect: after fetching remote refs into the cache,
175 | // the origin/feature ref remains locally even though it has been deleted upstream, since only the other overload prunes stale remotes:
176 | Util.deleteRecursive(new File(r.jenkins.getRootDir(), "caches"));
177 | }
178 | WorkflowRun b2 = r.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get());
179 | r.assertLogContains("nondeterministic checkout", b2); // SCMBinder
180 | r.assertLogContains("Could not determine exact tip revision of feature", b2); // SCMVar
181 | mp.scheduleBuild2(0).getFuture().get();
182 | WorkflowMultiBranchProjectTest.showIndexing(mp);
183 | assertEquals(2, mp.getItems().size());
184 | assertTrue(p.getACL().hasPermission2(auth, Item.DELETE));
185 | assertFalse(p.isBuildable());
186 | mp.setOrphanedItemStrategy(new DefaultOrphanedItemStrategy(true, "", "0"));
187 | mp.scheduleBuild2(0).getFuture().get();
188 | WorkflowMultiBranchProjectTest.showIndexing(mp);
189 | assertEquals(1, mp.getItems().size());
190 | }
191 |
192 | @Test public void untrustedRevisions() throws Exception {
193 | sampleGitRepo.init();
194 | sampleGitRepo.write("Jenkinsfile", "node {checkout scm; echo readFile('file')}");
195 | sampleGitRepo.write("file", "initial content");
196 | sampleGitRepo.git("add", "Jenkinsfile");
197 | sampleGitRepo.git("commit", "--all", "--message=flow");
198 | WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
199 | mp.getSourcesList().add(new BranchSource(new WarySource(null, sampleGitRepo.toString(), "", "*", "", false)));
200 | WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master");
201 | r.waitUntilNoActivity();
202 | WorkflowRun b = p.getLastBuild();
203 | assertNotNull(b);
204 | assertEquals(1, b.getNumber());
205 | assertRevisionAction(b);
206 | r.assertBuildStatusSuccess(b);
207 | r.assertLogContains("initial content", b);
208 | String branch = "some-other-branch-from-Norway";
209 | sampleGitRepo.git("checkout", "-b", branch);
210 | sampleGitRepo.write("Jenkinsfile", "error 'ALL YOUR BUILD STEPS ARE BELONG TO US'");
211 | sampleGitRepo.write("file", "subsequent content");
212 | sampleGitRepo.git("commit", "--all", "--message=big evil laugh");
213 | p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, branch);
214 | r.waitUntilNoActivity();
215 | b = p.getLastBuild();
216 | assertNotNull(b);
217 | assertEquals(1, b.getNumber());
218 | assertRevisionAction(b);
219 | r.assertBuildStatusSuccess(b);
220 | r.assertLogContains(Messages.ReadTrustedStep__has_been_modified_in_an_untrusted_revis("Jenkinsfile"), b);
221 | r.assertLogContains("subsequent content", b);
222 | r.assertLogContains("not trusting", b);
223 | }
224 | public static class WarySource extends GitSCMSource {
225 | public WarySource(String id, String remote, String credentialsId, String includes, String excludes, boolean ignoreOnPushNotifications) {
226 | super(id, remote, credentialsId, includes, excludes, ignoreOnPushNotifications);
227 | }
228 | @Override public SCMRevision getTrustedRevision(@NonNull SCMRevision revision, @NonNull TaskListener listener) throws IOException, InterruptedException {
229 | String branch = revision.getHead().getName();
230 | if (branch.equals("master")) {
231 | return revision;
232 | } else {
233 | listener.getLogger().println("not trusting " + branch);
234 | return fetch(new SCMHead("master"), listener);
235 | }
236 | }
237 | @Override public SCMSourceDescriptor getDescriptor() {
238 | return Jenkins.get().getDescriptorByType(GitSCMSource.DescriptorImpl.class);
239 | }
240 | }
241 |
242 | }
243 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/SCMVarTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2015 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.multibranch;
26 |
27 | import hudson.FilePath;
28 | import hudson.model.Run;
29 | import hudson.model.TaskListener;
30 | import java.io.File;
31 | import java.nio.file.Files;
32 | import java.util.Arrays;
33 | import jenkins.branch.BranchProperty;
34 | import jenkins.branch.BranchSource;
35 | import jenkins.branch.DefaultBranchPropertyStrategy;
36 | import jenkins.model.Jenkins;
37 | import jenkins.plugins.git.GitSCMSource;
38 | import jenkins.plugins.git.GitSampleRepoRule;
39 | import jenkins.plugins.git.GitStep;
40 | import org.apache.commons.io.FileUtils;
41 | import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition;
42 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
43 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
44 | import org.jenkinsci.plugins.workflow.libs.GlobalLibraries;
45 | import org.jenkinsci.plugins.workflow.libs.LibraryConfiguration;
46 | import org.jenkinsci.plugins.workflow.libs.LibraryRetriever;
47 | import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
48 | import org.junit.Test;
49 | import static org.junit.Assert.*;
50 | import org.junit.ClassRule;
51 | import org.junit.Rule;
52 | import org.jvnet.hudson.test.BuildWatcher;
53 | import org.jvnet.hudson.test.Issue;
54 | import org.jvnet.hudson.test.JenkinsSessionRule;
55 |
56 | public class SCMVarTest {
57 |
58 | @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
59 | @Rule public JenkinsSessionRule story = new JenkinsSessionRule();
60 | @Rule public GitSampleRepoRule sampleGitRepo = new GitSampleRepoRule();
61 |
62 | @Test public void scmPickle() throws Throwable {
63 | story.then(j -> {
64 | sampleGitRepo.init();
65 | sampleGitRepo.write("Jenkinsfile", "def _scm = scm; semaphore 'wait'; node {checkout _scm; echo readFile('file')}");
66 | sampleGitRepo.write("file", "initial content");
67 | sampleGitRepo.git("add", "Jenkinsfile");
68 | sampleGitRepo.git("commit", "--all", "--message=flow");
69 | WorkflowMultiBranchProject mp = j.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
70 | mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleGitRepo.toString(), "", "*", "", false), new DefaultBranchPropertyStrategy(new BranchProperty[0])));
71 | WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master");
72 | SemaphoreStep.waitForStart("wait/1", null);
73 | WorkflowRun b1 = p.getLastBuild();
74 | assertNotNull(b1);
75 | });
76 | story.then(j -> {
77 | SemaphoreStep.success("wait/1", null);
78 | WorkflowJob p = j.jenkins.getItemByFullName("p/master", WorkflowJob.class);
79 | assertNotNull(p);
80 | WorkflowRun b1 = p.getLastBuild();
81 | assertNotNull(b1);
82 | assertEquals(1, b1.getNumber());
83 | j.assertLogContains("initial content", j.waitForCompletion(b1));
84 | SCMBinderTest.assertRevisionAction(b1);
85 | });
86 | }
87 |
88 | @Issue("JENKINS-30222")
89 | @Test public void globalVariable() throws Throwable {
90 | story.then(j -> {
91 | // Set up a standardJob definition:
92 | File lib = new File(Jenkins.get().getRootDir(), "somelib");
93 | LibraryConfiguration cfg = new LibraryConfiguration("somelib", new LocalRetriever(lib));
94 | cfg.setImplicit(true);
95 | cfg.setDefaultVersion("fixed");
96 | GlobalLibraries.get().setLibraries(Arrays.asList(cfg));
97 | File vars = new File(lib, "vars");
98 | Files.createDirectories(vars.toPath());
99 | FileUtils.writeStringToFile(new File(vars, "standardJob.groovy"),
100 | "def call(body) {\n" +
101 | " def config = [:]\n" +
102 | " body.resolveStrategy = Closure.DELEGATE_FIRST\n" +
103 | " body.delegate = config\n" +
104 | " body()\n" +
105 | " node {\n" +
106 | " checkout scm\n" +
107 | " echo \"loaded ${readFile config.file}\"\n" +
108 | " }\n" +
109 | "}\n");
110 | // Then a project using it:
111 | sampleGitRepo.init();
112 | sampleGitRepo.write("Jenkinsfile", "standardJob {file = 'resource'}");
113 | sampleGitRepo.write("resource", "resource content");
114 | sampleGitRepo.git("add", "Jenkinsfile");
115 | sampleGitRepo.git("add", "resource");
116 | sampleGitRepo.git("commit", "--all", "--message=flow");
117 | // And run:
118 | WorkflowMultiBranchProject mp = j.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
119 | mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleGitRepo.toString(), "", "*", "", false), new DefaultBranchPropertyStrategy(new BranchProperty[0])));
120 | WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master");
121 | WorkflowRun b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
122 | j.assertLogContains("loaded resource content", b);
123 | });
124 | }
125 |
126 | // TODO copied from GrapeTest along with body of libroot(); could make sense as a *-tests.jar utility
127 | private static final class LocalRetriever extends LibraryRetriever {
128 | private final File lib;
129 | LocalRetriever(File lib) {
130 | this.lib = lib;
131 | }
132 | @Override public void retrieve(String name, String version, boolean changelog, FilePath target, Run, ?> run, TaskListener listener) throws Exception {
133 | new FilePath(lib).copyRecursiveTo(target);
134 | }
135 | @Override public void retrieve(String name, String version, FilePath target, Run, ?> run, TaskListener listener) throws Exception {
136 | retrieve(name, version, false, target, run, listener);
137 | }
138 | }
139 |
140 | @Issue("JENKINS-31386")
141 | @Test public void standaloneProject() throws Throwable {
142 | story.then(j -> {
143 | sampleGitRepo.init();
144 | sampleGitRepo.write("Jenkinsfile", "node {checkout scm; echo readFile('file')}");
145 | sampleGitRepo.write("file", "some content");
146 | sampleGitRepo.git("add", "Jenkinsfile");
147 | sampleGitRepo.git("commit", "--all", "--message=flow");
148 | WorkflowJob p = j.jenkins.createProject(WorkflowJob.class, "p");
149 | p.setDefinition(new CpsScmFlowDefinition(new GitStep(sampleGitRepo.toString()).createSCM(), "Jenkinsfile"));
150 | WorkflowRun b = j.assertBuildStatusSuccess(p.scheduleBuild2(0));
151 | j.assertLogContains("some content", b);
152 | });
153 | }
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/WorkflowBranchProjectFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2015 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.multibranch;
26 |
27 | import java.io.File;
28 | import java.util.Collections;
29 | import jenkins.branch.BranchSource;
30 | import jenkins.plugins.git.GitSCMSource;
31 | import jenkins.plugins.git.GitSampleRepoRule;
32 | import jenkins.plugins.git.traits.BranchDiscoveryTrait;
33 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
34 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
35 | import static org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject;
36 | import org.junit.Test;
37 | import static org.junit.Assert.*;
38 | import org.junit.ClassRule;
39 | import org.junit.Rule;
40 | import org.jvnet.hudson.test.BuildWatcher;
41 | import org.jvnet.hudson.test.Issue;
42 | import org.jvnet.hudson.test.JenkinsRule;
43 | import org.jvnet.hudson.test.JenkinsSessionRule;
44 |
45 | public class WorkflowBranchProjectFactoryTest {
46 |
47 | @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
48 | @Rule public JenkinsSessionRule story = new JenkinsSessionRule();
49 | @Rule public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
50 |
51 | @Issue("JENKINS-30744")
52 | @Test public void slashyBranches() throws Throwable {
53 | story.then(j -> {
54 | sampleRepo.init();
55 | sampleRepo.git("checkout", "-b", "dev/main");
56 | String script =
57 | "echo \"branch=${env.BRANCH_NAME}\"\n" +
58 | "node {\n" +
59 | " checkout scm\n" +
60 | " echo \"workspace=${pwd().replaceFirst('.+dev', 'dev')}\"\n" +
61 | "}";
62 | sampleRepo.write("Jenkinsfile", script);
63 | sampleRepo.git("add", "Jenkinsfile");
64 | sampleRepo.git("commit", "--all", "--message=flow");
65 | WorkflowMultiBranchProject mp = j.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
66 | GitSCMSource source = new GitSCMSource(sampleRepo.toString());
67 | source.setTraits(Collections.singletonList(new BranchDiscoveryTrait()));
68 | mp.getSourcesList().add(new BranchSource(source));
69 | WorkflowJob p = scheduleAndFindBranchProject(mp, "dev%2Fmain");
70 | assertEquals(1, mp.getItems().size());
71 | j.waitUntilNoActivity();
72 | WorkflowRun b1 = p.getLastBuild();
73 | assertEquals(1, b1.getNumber());
74 | j.assertLogContains("branch=dev/main", b1);
75 | j.assertLogContains("workspace=dev_main", b1);
76 | verifyProject(j, p);
77 | sampleRepo.write("Jenkinsfile", script.replace("branch=", "Branch="));
78 | });
79 | story.then(j -> {
80 | WorkflowJob p = j.jenkins.getItemByFullName("p/dev%2Fmain", WorkflowJob.class);
81 | assertNotNull(p);
82 | sampleRepo.git("commit", "--all", "--message=Flow");
83 | sampleRepo.notifyCommit(j);
84 | WorkflowRun b2 = p.getLastBuild();
85 | assertEquals(2, b2.getNumber());
86 | j.assertLogContains("Branch=dev/main", b2);
87 | j.assertLogContains("workspace=dev_main", b2);
88 | verifyProject(j, p);
89 | });
90 | }
91 | private static void verifyProject(JenkinsRule j, WorkflowJob p) throws Exception {
92 | assertEquals("dev%2Fmain", p.getName());
93 | assertEquals("dev/main", p.getDisplayName());
94 | assertEquals("p/dev%2Fmain", p.getFullName());
95 | assertEquals("p » dev/main", p.getFullDisplayName());
96 | j.createWebClient().getPage(p);
97 | assertEquals(new File(new File(p.getParent().getRootDir(), "branches"), "dev-main.k31kdj"), p.getRootDir());
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/test/java/org/jenkinsci/plugins/workflow/multibranch/WorkflowMultiBranchProjectFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2015 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package org.jenkinsci.plugins.workflow.multibranch;
26 |
27 | import hudson.model.Item;
28 | import hudson.model.User;
29 | import hudson.model.View;
30 | import hudson.security.ACL;
31 | import hudson.security.FullControlOnceLoggedInAuthorizationStrategy;
32 | import java.io.File;
33 | import java.util.List;
34 | import jenkins.branch.MultiBranchProject;
35 | import jenkins.branch.OrganizationFolder;
36 | import jenkins.plugins.git.GitSampleRepoRule;
37 | import jenkins.scm.api.SCMSource;
38 | import org.springframework.security.core.Authentication;
39 | import static org.hamcrest.Matchers.*;
40 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
41 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
42 | import static org.junit.Assert.*;
43 | import org.junit.ClassRule;
44 | import org.junit.Rule;
45 | import org.junit.Test;
46 | import org.junit.rules.TemporaryFolder;
47 | import org.jvnet.hudson.test.BuildWatcher;
48 | import org.jvnet.hudson.test.Issue;
49 | import org.jvnet.hudson.test.JenkinsRule;
50 |
51 | public class WorkflowMultiBranchProjectFactoryTest {
52 |
53 | @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
54 | @Rule public JenkinsRule r = new JenkinsRule();
55 | @Rule public GitSampleRepoRule sampleRepo1 = new GitSampleRepoRule();
56 | @Rule public GitSampleRepoRule sampleRepo2 = new GitSampleRepoRule();
57 | @Rule public GitSampleRepoRule sampleRepo3 = new GitSampleRepoRule();
58 | @Rule public TemporaryFolder tmp = new TemporaryFolder();
59 |
60 | @Test public void smokes() throws Exception {
61 | File clones = tmp.newFolder();
62 | sampleRepo1.init();
63 | sampleRepo1.write(WorkflowBranchProjectFactory.SCRIPT, "echo 'ran one'");
64 | sampleRepo1.git("add", WorkflowBranchProjectFactory.SCRIPT);
65 | sampleRepo1.git("commit", "--all", "--message=flow");
66 | sampleRepo1.git("clone", ".", new File(clones, "one").getAbsolutePath());
67 | sampleRepo3.init(); // but do not write SCRIPT, so should be ignored
68 | sampleRepo3.git("clone", ".", new File(clones, "three").getAbsolutePath());
69 | r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
70 | r.jenkins.setAuthorizationStrategy(new FullControlOnceLoggedInAuthorizationStrategy());
71 | OrganizationFolder top = r.jenkins.createProject(OrganizationFolder.class, "top");
72 | top.getNavigators().add(new GitDirectorySCMNavigator(clones.getAbsolutePath()));
73 | // Make sure we created one multibranch projects:
74 | top.scheduleBuild2(0).getFuture().get();
75 | top.getComputation().writeWholeLogTo(System.out);
76 | assertEquals(1, top.getItems().size());
77 | MultiBranchProject,?> one = top.getItem("one");
78 | assertThat(one, is(instanceOf(WorkflowMultiBranchProject.class)));
79 | // Check that it has Git configured:
80 | List sources = one.getSCMSources();
81 | assertEquals(1, sources.size());
82 | assertEquals("GitSCMSource", sources.get(0).getClass().getSimpleName());
83 | // Verify permissions:
84 | Authentication admin = User.getById("admin", true).impersonate2();
85 | ACL acl = one.getACL();
86 | assertTrue(acl.hasPermission2(ACL.SYSTEM2, Item.CONFIGURE));
87 | assertTrue(acl.hasPermission2(ACL.SYSTEM2, Item.DELETE));
88 | assertFalse(acl.hasPermission2(admin, Item.CONFIGURE));
89 | assertFalse(acl.hasPermission2(admin, View.CONFIGURE));
90 | assertFalse(acl.hasPermission2(admin, View.CREATE));
91 | assertFalse(acl.hasPermission2(admin, View.DELETE));
92 | assertFalse(acl.hasPermission2(admin, Item.DELETE));
93 | assertTrue(acl.hasPermission2(admin, Item.EXTENDED_READ));
94 | assertTrue(acl.hasPermission2(admin, Item.READ));
95 | assertTrue(acl.hasPermission2(admin, View.READ));
96 | // Check that the master branch project works:
97 | r.waitUntilNoActivity();
98 | WorkflowJob p = WorkflowMultiBranchProjectTest.findBranchProject((WorkflowMultiBranchProject) one, "master");
99 | WorkflowRun b1 = p.getLastBuild();
100 | assertEquals(1, b1.getNumber());
101 | r.assertLogContains("ran one", b1);
102 | // Then add a second checkout and reindex:
103 | sampleRepo2.init();
104 | sampleRepo2.write(WorkflowBranchProjectFactory.SCRIPT, "echo 'ran two'");
105 | sampleRepo2.git("add", WorkflowBranchProjectFactory.SCRIPT);
106 | sampleRepo2.git("commit", "--all", "--message=flow");
107 | sampleRepo2.git("clone", ".", new File(clones, "two").getAbsolutePath());
108 | top.scheduleBuild2(0).getFuture().get();
109 | top.getComputation().writeWholeLogTo(System.out);
110 | assertEquals(2, top.getItems().size());
111 | // Same for another one:
112 | MultiBranchProject,?> two = top.getItem("two");
113 | assertThat(two, is(instanceOf(WorkflowMultiBranchProject.class)));
114 | r.waitUntilNoActivity();
115 | p = WorkflowMultiBranchProjectTest.findBranchProject((WorkflowMultiBranchProject) two, "master");
116 | b1 = p.getLastBuild();
117 | assertEquals(1, b1.getNumber());
118 | r.assertLogContains("ran two", b1);
119 | // JENKINS-34246: also delete Jenkinsfile
120 | sampleRepo2.git("rm", WorkflowBranchProjectFactory.SCRIPT);
121 | sampleRepo2.git("commit", "--message=noflow");
122 | top.scheduleBuild2(0).getFuture().get();
123 | top.getComputation().writeWholeLogTo(System.out);
124 | assertEquals(1, top.getItems().size());
125 | }
126 |
127 | @Issue("JENKINS-34561")
128 | @Test public void configuredScriptName() throws Exception {
129 | String alternativeJenkinsFileName = "alternative_Jenkinsfile_name.groovy";
130 |
131 | File clones = tmp.newFolder();
132 | sampleRepo1.init();
133 | sampleRepo1.write(WorkflowBranchProjectFactory.SCRIPT,
134 | "echo 'echo from " + WorkflowBranchProjectFactory.SCRIPT + "'");
135 | sampleRepo1.git("add", WorkflowBranchProjectFactory.SCRIPT);
136 | sampleRepo1.git("commit", "--all", "--message=flow");
137 | String repoWithJenkinsfile = "repo_with_jenkinsfile";
138 | sampleRepo1.git("clone", ".", new File(clones, repoWithJenkinsfile).getAbsolutePath());
139 |
140 | r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
141 | r.jenkins.setAuthorizationStrategy(new FullControlOnceLoggedInAuthorizationStrategy());
142 | OrganizationFolder top = r.jenkins.createProject(OrganizationFolder.class, "top");
143 | top.getNavigators().add(new GitDirectorySCMNavigator(clones.getAbsolutePath()));
144 | top.scheduleBuild2(0).getFuture().get();
145 | top.getComputation().writeWholeLogTo(System.out);
146 | assertEquals(1, top.getItems().size());
147 |
148 | // Make sure we created one multibranch project:
149 | top.scheduleBuild2(0).getFuture().get();
150 | top.getComputation().writeWholeLogTo(System.out);
151 | assertEquals(1, top.getItems().size());
152 | MultiBranchProject,?> projectFromJenkinsfile = top.getItem(repoWithJenkinsfile);
153 | assertThat(projectFromJenkinsfile, is(instanceOf(WorkflowMultiBranchProject.class)));
154 |
155 | // Check that the 'Jenkinsfile' project works:
156 | r.waitUntilNoActivity();
157 | WorkflowJob p = WorkflowMultiBranchProjectTest.findBranchProject((WorkflowMultiBranchProject) projectFromJenkinsfile, "master");
158 | WorkflowRun b1 = p.getLastBuild();
159 | assertEquals(1, b1.getNumber());
160 | r.assertLogContains("echo from Jenkinsfile", b1);
161 |
162 | // add second Project Recognizer with alternative Jenkinsfile name to organization folder
163 | WorkflowMultiBranchProjectFactory workflowMultiBranchProjectFactory = new WorkflowMultiBranchProjectFactory();
164 | workflowMultiBranchProjectFactory.setScriptPath(alternativeJenkinsFileName);
165 | top.getProjectFactories().add(workflowMultiBranchProjectFactory);
166 |
167 | // Then add a second checkout and reindex:
168 | sampleRepo2.init();
169 | sampleRepo2.write(alternativeJenkinsFileName,
170 | "echo 'echo from " + alternativeJenkinsFileName + "'");
171 | sampleRepo2.git("add", alternativeJenkinsFileName);
172 | sampleRepo2.git("commit", "--all", "--message=flow");
173 | String repoWithAlternativeJenkinsfile = "repo_with_alternative_jenkinsfile";
174 | sampleRepo2.git("clone", ".", new File(clones, repoWithAlternativeJenkinsfile).getAbsolutePath());
175 |
176 | // Make sure we created two multibranch projects:
177 | top.scheduleBuild2(0).getFuture().get();
178 | top.getComputation().writeWholeLogTo(System.out);
179 | assertEquals(2, top.getItems().size());
180 |
181 | // Check that the 'alternative_Jenkinsfile_name' project works:
182 | MultiBranchProject,?> projectFromAlternativeJenkinsFile = top.getItem(repoWithAlternativeJenkinsfile);
183 | assertThat(projectFromAlternativeJenkinsFile, is(instanceOf(WorkflowMultiBranchProject.class)));
184 | r.waitUntilNoActivity();
185 | p = WorkflowMultiBranchProjectTest.findBranchProject((WorkflowMultiBranchProject) projectFromAlternativeJenkinsFile, "master");
186 | b1 = p.getLastBuild();
187 | assertEquals(1, b1.getNumber());
188 | r.assertLogContains("echo from alternative_Jenkinsfile_name", b1);
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/test/resources/org/jenkinsci/plugins/workflow/multibranch/GitDirectorySCMNavigator/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/test/resources/org/jenkinsci/plugins/workflow/multibranch/JobPropertyStepTest/trackerPropertyUpgrade.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/workflow-multibranch-plugin/bb688f609ee91a5a2334caa75063d368cde173c5/src/test/resources/org/jenkinsci/plugins/workflow/multibranch/JobPropertyStepTest/trackerPropertyUpgrade.zip
--------------------------------------------------------------------------------
/src/test/resources/org/jenkinsci/plugins/workflow/multibranch/RepairBranchPropertyTest/removedPropertyAtStartup.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/workflow-multibranch-plugin/bb688f609ee91a5a2334caa75063d368cde173c5/src/test/resources/org/jenkinsci/plugins/workflow/multibranch/RepairBranchPropertyTest/removedPropertyAtStartup.zip
--------------------------------------------------------------------------------
/src/test/resources/org/jenkinsci/plugins/workflow/multibranch/WorkflowMultiBranchProjectTest/OldSCM/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------