-
Notifications
You must be signed in to change notification settings - Fork 168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for running promotions on inheritance projects. #75
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,21 +64,18 @@ public final class JobPropertyImpl extends JobProperty<AbstractProject<?,?>> imp | |
* These {@link PromotionProcess}es are active. | ||
*/ | ||
private final Set<String> activeProcessNames = new HashSet<String>(); | ||
|
||
// /** | ||
// * Names of the processes that are configured. | ||
// * Used to construct {@link #processes}. | ||
// */ | ||
// private final List<String> names = new ArrayList<String>(); | ||
|
||
/** | ||
* Programmatic construction. | ||
*/ | ||
public JobPropertyImpl(AbstractProject<?,?> owner) throws Descriptor.FormException, IOException { | ||
this.owner = owner; | ||
init(); | ||
} | ||
|
||
public JobPropertyImpl(JobPropertyImpl other, AbstractProject<?,?> owner) throws Descriptor.FormException, IOException { | ||
this.owner = owner; | ||
this.activeProcessNames.addAll(other.activeProcessNames); | ||
loadAllProcesses(other.getRootDir()); | ||
} | ||
private JobPropertyImpl(StaplerRequest req, JSONObject json) throws Descriptor.FormException, IOException { | ||
// a hack to get the owning AbstractProject. | ||
// this is needed here so that we can load items | ||
|
@@ -112,14 +109,25 @@ private JobPropertyImpl(StaplerRequest req, JSONObject json) throws Descriptor.F | |
} | ||
init(); | ||
} | ||
private void loadAllProcesses(File rootDir) throws IOException { | ||
File[] subdirs = rootDir.listFiles(new FileFilter() { | ||
public boolean accept(File child) { | ||
return child.isDirectory(); | ||
} | ||
}); | ||
|
||
loadProcesses(subdirs); | ||
} | ||
private void init() throws IOException { | ||
// load inactive processes | ||
File[] subdirs = getRootDir().listFiles(new FileFilter() { | ||
public boolean accept(File child) { | ||
return child.isDirectory() && !isActiveProcessNameIgnoreCase(child.getName()); | ||
} | ||
}); | ||
loadProcesses(subdirs); | ||
} | ||
private void loadProcesses(File[] subdirs) throws IOException { | ||
if(subdirs!=null) { | ||
for (File subdir : subdirs) { | ||
try { | ||
|
@@ -179,7 +187,7 @@ protected synchronized void setOwner(AbstractProject<?,?> owner) { | |
// so use this as the initialization opportunity. | ||
// CopyListener is also using setOwner to re-init after copying config from another job. | ||
processes = new ArrayList<PromotionProcess>(ItemGroupMixIn.<String,PromotionProcess>loadChildren( | ||
this,getRootDir(),ItemGroupMixIn.KEYED_BY_NAME).values()); | ||
this,getRootDir(),ItemGroupMixIn.KEYED_BY_NAME).values()); | ||
try { | ||
buildActiveProcess(); | ||
} catch (IOException e) { | ||
|
@@ -201,7 +209,11 @@ private void buildActiveProcess() throws IOException { | |
// ensure that the name casing matches what's given in the activeProcessName | ||
// this is because in case insensitive file system, we may end up resolving | ||
// to a directory name that differs only in their case. | ||
p.renameTo(getActiveProcessName(p.getName())); | ||
String processName = p.getName(); | ||
String activeProcessName = getActiveProcessName(processName); | ||
if (!activeProcessName.equals(processName)){ | ||
p.renameTo(activeProcessName); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change seems to be unrelated to the issue, but seems to be reasonable. Anyway, the plugin would benefit from a configurable case sensitivity handling like it happens with User IDs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is actually related to the issue. Unfortunately p.renameTo results in an attempt to lock the promotion process and ultimately a deadlock. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed |
||
} | ||
} | ||
|
||
|
@@ -352,16 +364,16 @@ public Action getJobAction(AbstractProject<?,?> job) { | |
|
||
@Extension | ||
public static final class DescriptorImpl extends JobPropertyDescriptor { | ||
|
||
public DescriptorImpl() { | ||
super(); | ||
} | ||
super(); | ||
} | ||
|
||
public DescriptorImpl(Class<? extends JobProperty<?>> clazz) { | ||
super(clazz); | ||
} | ||
public DescriptorImpl(Class<? extends JobProperty<?>> clazz) { | ||
super(clazz); | ||
} | ||
|
||
public String getDisplayName() { | ||
public String getDisplayName() { | ||
return "Promote Builds When..."; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
|
||
package hudson.plugins.promoted_builds.inheritance; | ||
|
||
import org.apache.log4j.Logger; | ||
|
||
import hudson.Extension; | ||
import hudson.model.JobProperty; | ||
|
||
import hudson.plugins.project_inheritance.projects.InheritanceProject; | ||
import hudson.plugins.project_inheritance.projects.inheritance.InheritanceSelector; | ||
|
||
import hudson.plugins.promoted_builds.JobPropertyImpl; | ||
|
||
/** | ||
* | ||
* @author Jacek Tomaka | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A good practice is to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. Will add this. Thanks. |
||
* @since TODO | ||
*/ | ||
@Extension(optional=true) | ||
public class JobPropertyImplSelector extends InheritanceSelector<JobProperty<?>> { | ||
private static final long serialVersionUID = 1L; | ||
private static final Logger logger = Logger.getLogger(JobPropertyImplSelector.class); | ||
|
||
@Override | ||
public boolean isApplicableFor(Class<?> clazz){ | ||
return JobProperty.class.isAssignableFrom(clazz); | ||
} | ||
|
||
@Override | ||
public InheritanceSelector.MODE getModeFor(Class<?> clazz){ | ||
if (JobPropertyImpl.class.isAssignableFrom(clazz)) return MODE.USE_LAST; | ||
return MODE.NOT_RESPONSIBLE; | ||
} | ||
|
||
@Override | ||
public String getObjectIdentifier(JobProperty<?> obj){ | ||
if ( obj!=null && JobPropertyImpl.class.getName().equals(obj.getClass().getName())){ | ||
return JobPropertyImplSelector.class.getName(); | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public JobPropertyImpl merge(JobProperty<?> prior, JobProperty<?> latter, InheritanceProject caller){ | ||
return null; | ||
} | ||
|
||
@Override | ||
public JobProperty<?> handleSingleton(JobProperty<?> jobProperty, InheritanceProject caller){ | ||
if (jobProperty == null || caller == null) return jobProperty; | ||
if (caller.isAbstract) return jobProperty; | ||
|
||
if (!JobPropertyImpl.class.isAssignableFrom(jobProperty.getClass())) return jobProperty; | ||
|
||
|
||
JobPropertyImpl jobPropertyImpl = (JobPropertyImpl)jobProperty; | ||
|
||
try { | ||
JobPropertyImpl newJobProperty = new JobPropertyImpl(jobPropertyImpl, caller); | ||
return newJobProperty; | ||
} catch (Exception ex){ | ||
logger.error("Error during hacking up JobPropertyImpl", ex ); | ||
} | ||
return jobProperty; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* The MIT License | ||
* | ||
* Copyright 2015 Franta Mejta | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* copies of the Software, and to permit persons to whom the Software is | ||
* furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in | ||
* all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
* THE SOFTWARE. | ||
*/ | ||
package hudson.plugins.promoted_builds.conditions.inheritance; | ||
|
||
import static org.junit.Assert.*; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.jvnet.hudson.test.Bug; | ||
|
||
import hudson.model.Result; | ||
import hudson.plugins.project_inheritance.projects.InheritanceBuild; | ||
import hudson.plugins.project_inheritance.projects.InheritanceProject.IMode; | ||
import hudson.plugins.promoted_builds.JobPropertyImpl; | ||
import hudson.plugins.promoted_builds.PromotedBuildAction; | ||
import hudson.plugins.promoted_builds.PromotionProcess; | ||
import hudson.plugins.promoted_builds.Status; | ||
import hudson.plugins.promoted_builds.conditions.DownstreamPassCondition; | ||
import hudson.plugins.promoted_builds.inheritance.helpers.InheritanceProjectRule; | ||
import hudson.plugins.promoted_builds.inheritance.helpers.InheritanceProjectsPair; | ||
import hudson.tasks.BuildTrigger; | ||
import jenkins.model.Jenkins; | ||
|
||
public final class DownstreamPassConditionInheritanceTest { | ||
|
||
@Rule | ||
public InheritanceProjectRule j = new InheritanceProjectRule(); | ||
|
||
@Test | ||
@Bug(7739) | ||
public void shouldEvaluateUpstreamRecursively() throws Exception { | ||
final InheritanceProjectsPair pair1 = j.createInheritanceProjectDerivedWithBase(); | ||
final InheritanceProjectsPair pair2 = j.createInheritanceProjectDerivedWithBase(); | ||
final InheritanceProjectsPair pair3 = j.createInheritanceProjectDerivedWithBase(); | ||
|
||
|
||
final JobPropertyImpl property = new JobPropertyImpl(pair1.getBase()); | ||
pair1.getBase().addProperty(property); | ||
|
||
final PromotionProcess process = property.addProcess("promotion"); | ||
process.conditions.add(new DownstreamPassCondition(pair3.getDerived().getFullName())); | ||
|
||
pair1.getDerived().getPublishersList().add(new BuildTrigger(pair2.getDerived().getFullName(), Result.SUCCESS)); | ||
pair2.getDerived().getPublishersList().add(new BuildTrigger(pair3.getDerived().getFullName(), Result.SUCCESS)); | ||
Jenkins.getInstance().rebuildDependencyGraph(); | ||
|
||
final InheritanceBuild run1 = j.buildAndAssertSuccess(pair1.getDerived()); | ||
j.assertBuildStatusSuccess(run1); | ||
j.waitUntilNoActivity(); | ||
j.assertBuildStatusSuccess(pair2.getDerived().getLastBuild()); | ||
j.waitUntilNoActivity(); | ||
final InheritanceBuild run3 = j.assertBuildStatusSuccess(pair3.getDerived().getLastBuild()); | ||
j.waitUntilNoActivity(); | ||
|
||
//We cannot assume that the process will contain builds because the process added to base project is different to the one in derived. | ||
JobPropertyImpl jobProperty = pair1.getDerived().getProperty(JobPropertyImpl.class, | ||
/*Forcing inheritance as temporary hack for inheritance plugin 1.53 | ||
because that version of the plugin uses inheritance only for certain predefined cases: | ||
-specific methods on the call stack | ||
-url paths. | ||
This has been changed as pull request https://github.com/i-m-c/jenkins-inheritance-plugin/pull/40 | ||
*/ | ||
IMode.INHERIT_FORCED); | ||
|
||
assertNotNull("derived jobProperty is null", jobProperty); | ||
PromotionProcess processDerived = jobProperty.getItem("promotion"); | ||
|
||
assertEquals("fingerprint relation", run3.getUpstreamRelationship(pair1.getDerived()), -1); | ||
assertFalse("no promotion process", processDerived.getBuilds().isEmpty()); | ||
|
||
final PromotedBuildAction action = run1.getAction(PromotedBuildAction.class); | ||
assertNotNull("no promoted action", action); | ||
|
||
final Status promotion = action.getPromotion("promotion"); | ||
assertNotNull("promotion not found", promotion); | ||
assertTrue("promotion not successful", promotion.isPromotionSuccessful()); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly duplicates
init()
. Would be great to merge methods somehow