Skip to content

Commit

Permalink
Merge pull request #45 from ikedam/feature/ItemGroupSupport
Browse files Browse the repository at this point in the history
  Item Group Support (improvement of #43), including fix for JENKINS-19793 (#44)
  • Loading branch information
ikedam committed Oct 5, 2013
2 parents 7827fd5 + 0c6d344 commit 5534eb0
Show file tree
Hide file tree
Showing 7 changed files with 589 additions and 40 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.466</version>
<version>1.480</version>
</parent>

<artifactId>parameterized-trigger</artifactId>
Expand Down
Expand Up @@ -190,7 +190,7 @@ public void buildEnvVars(AbstractBuild<?, ?> build, EnvVars env) {
* were non blocking, which we don't have a builds for. Used in the UI for see
* Summary.groovy
*
* @return a list of builds that are triggered by this build
* @return a list of builds that are triggered by this build. May contains null if a project or a build is deleted.
*/
public List<AbstractBuild<?, ?>> getTriggeredBuilds() {

Expand All @@ -201,7 +201,7 @@ public void buildEnvVars(AbstractBuild<?, ?> build, EnvVars env) {
Jenkins.getInstance().getItemByFullName(projectName, AbstractProject.class);
for (BuildReference br : this.buildRefs.get(projectName)) {
if (br.buildNumber != 0) {
builds.add(project.getBuildByNumber(br.buildNumber));
builds.add((project != null)?project.getBuildByNumber(br.buildNumber):null);
}
}
}
Expand All @@ -213,7 +213,7 @@ public void buildEnvVars(AbstractBuild<?, ?> build, EnvVars env) {
* which we don't have a builds for. Does not include builds that are returned
* in #link{getTriggeredBuilds} Used in the UI for see Summary.groovy
*
* @return List of Projects that are triggered by this build
* @return List of Projects that are triggered by this build. May contains null if a project is deleted.
*/
public List<AbstractProject<?, ?>> getTriggeredProjects() {
List<AbstractProject<?, ?>> projects = new ArrayList<AbstractProject<?, ?>>();
Expand Down
Expand Up @@ -32,6 +32,7 @@
import hudson.plugins.promoted_builds.Promotion;
import hudson.tasks.Messages;
import hudson.util.FormValidation;
import hudson.util.VersionNumber;

import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
Expand Down Expand Up @@ -454,31 +455,88 @@ protected Future schedule(AbstractBuild<?, ?> build, AbstractProject project, Li
return schedule(build, project, project.getQuietPeriod(), list);
}

public boolean onJobRenamed(String oldName, String newName) {
boolean changed = false;
String[] list = projects.split(",");
for (int i = 0; i < list.length; i++) {
if (list[i].trim().equals(oldName)) {
list[i] = newName;
changed = true;
}
}
if (changed) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < list.length; i++) {
if (list[i] == null) continue;
if (buf.length() > 0){
buf.append(',');
}
buf.append(list[i]);
}
projects = buf.toString();
}
/**
* A backport of {@link Items#computeRelativeNamesAfterRenaming(String, String, String, ItemGroup)} in Jenkins 1.530.
*
* computeRelativeNamesAfterRenaming contains a bug in Jenkins < 1.530.
* Replace this to {@link Items#computeRelativeNamesAfterRenaming(String, String, String, ItemGroup)}
* when updated the target version to >= 1.530.
*
* @param oldFullName
* @param newFullName
* @param relativeNames
* @param context
* @return
*/
private static String computeRelativeNamesAfterRenaming(String oldFullName, String newFullName, String relativeNames, ItemGroup<?> context) {
if(!Jenkins.getVersion().isOlderThan(new VersionNumber("1.530"))) {
return Items.computeRelativeNamesAfterRenaming(oldFullName, newFullName, relativeNames, context);
}
StringTokenizer tokens = new StringTokenizer(relativeNames,",");
List<String> newValue = new ArrayList<String>();
while(tokens.hasMoreTokens()) {
String relativeName = tokens.nextToken().trim();
String canonicalName = Items.getCanonicalName(context, relativeName);
if (canonicalName.equals(oldFullName) || canonicalName.startsWith(oldFullName + "/")) {
String newCanonicalName = newFullName + canonicalName.substring(oldFullName.length());
// relative name points to the renamed item, let's compute the new relative name
newValue.add( computeRelativeNameAfterRenaming(canonicalName, newCanonicalName, relativeName) );
} else {
newValue.add(relativeName);
}
}
return StringUtils.join(newValue, ",");
}

private static String computeRelativeNameAfterRenaming(String oldFullName, String newFullName, String relativeName) {

String[] a = oldFullName.split("/");
String[] n = newFullName.split("/");
assert a.length == n.length;
String[] r = relativeName.split("/");

int j = a.length-1;
for(int i=r.length-1;i>=0;i--) {
String part = r[i];
if (part.equals("") && i==0) {
continue;
}
if (part.equals(".")) {
continue;
}
if (part.equals("..")) {
j--;
continue;
}
if (part.equals(a[j])) {
r[i] = n[j];
j--;
continue;
}
}
return StringUtils.join(r, '/');
}

public boolean onJobRenamed(ItemGroup context, String oldName, String newName) {
String newProjects = computeRelativeNamesAfterRenaming(oldName, newName, projects, context);
boolean changed = !projects.equals(newProjects);
projects = newProjects;
return changed;
}

public boolean onDeleted(String oldName) {
return onJobRenamed(oldName, null);
public boolean onDeleted(ItemGroup context, String oldName) {
List<String> newNames = new ArrayList<String>();
StringTokenizer tokens = new StringTokenizer(projects,",");
List<String> newValue = new ArrayList<String>();
while (tokens.hasMoreTokens()) {
String relativeName = tokens.nextToken().trim();
String fullName = Items.getCanonicalName(context, relativeName);
if (!fullName.equals(oldName)) newNames.add(relativeName);
}
String newProjects = StringUtils.join(newNames, ",");
boolean changed = !projects.equals(newProjects);
projects = newProjects;
return changed;
}

public Descriptor<BuildTriggerConfig> getDescriptor() {
Expand Down Expand Up @@ -549,13 +607,14 @@ public FormValidation doCheckProjects(@AncestorInPath Item project, @QueryParame
* @param value
* @return
*/
public AutoCompletionCandidates doAutoCompleteProjects(@QueryParameter String value) {
public AutoCompletionCandidates doAutoCompleteProjects(@QueryParameter String value, @AncestorInPath ItemGroup context) {
AutoCompletionCandidates candidates = new AutoCompletionCandidates();
List<Job> jobs = Hudson.getInstance().getItems(Job.class);
List<Job> jobs = Jenkins.getInstance().getAllItems(Job.class);
for (Job job: jobs) {
if (job.getFullName().startsWith(value)) {
String relativeName = job.getRelativeNameFrom(context);
if (relativeName.startsWith(value)) {
if (job.hasPermission(Item.READ)) {
candidates.add(job.getFullName());
candidates.add(relativeName);
}
}
}
Expand Down
28 changes: 18 additions & 10 deletions src/main/java/hudson/plugins/parameterizedtrigger/Plugin.java
Expand Up @@ -3,6 +3,8 @@
import hudson.Extension;
import hudson.model.Item;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Project;
import hudson.model.listeners.ItemListener;
import hudson.util.EnumConverter;
Expand All @@ -18,6 +20,8 @@
import org.jenkinsci.plugins.conditionalbuildstep.ConditionalBuildStepHelper;
import org.kohsuke.stapler.Stapler;

import static org.apache.commons.lang.StringUtils.isEmpty;

public class Plugin extends hudson.Plugin {

@Override
Expand All @@ -33,20 +37,24 @@ public void start() throws Exception {
public static final class RenameListener extends ItemListener {
@Override
public void onRenamed(Item item, String oldName, String newName) {
for (Project<?,?> p : Hudson.getInstance().getProjects()) {
ItemGroup context = item.getParent();
String full = isEmpty(context.getFullName()) ? "" : context.getFullName() + '/';
String fullOldName = full + oldName;
String fullNewName = full + newName;
for (Project<?,?> p : Jenkins.getInstance().getAllItems(Project.class)) {
boolean changed = false;
//iterate over post build triggers
BuildTrigger bt = p.getPublishersList().get(BuildTrigger.class);
if (bt != null) {
for (BuildTriggerConfig c : bt.getConfigs()){
changed |= c.onJobRenamed(oldName, newName);
changed |= c.onJobRenamed(p.getParent(), fullOldName, fullNewName);
}
}
//iterate over build step triggers
TriggerBuilder tb = p.getBuildersList().get(TriggerBuilder.class);
if (tb != null) {
for (BuildTriggerConfig co : tb.getConfigs()){
changed |= co.onJobRenamed(oldName, newName);
changed |= co.onJobRenamed(p.getParent(), fullOldName, fullNewName);
}
}

Expand All @@ -61,7 +69,7 @@ public void onRenamed(Item item, String oldName, String newName) {
p.save();
} catch (IOException e) {
Logger.getLogger(RenameListener.class.getName()).log(Level.WARNING,
"Failed to persist project setting during rename from "+oldName+" to "+newName, e);
"Failed to persist project setting during rename from "+fullOldName+" to "+fullNewName, e);
}
}

Expand All @@ -70,15 +78,15 @@ public void onRenamed(Item item, String oldName, String newName) {

@Override
public void onDeleted(Item item) {
for (Project<?,?> p : Hudson.getInstance().getProjects()) {
String oldName = item.getName();
for (Project<?,?> p : Jenkins.getInstance().getAllItems(Project.class)) {
String oldName = item.getFullName();
boolean changed = false;
//iterate over post build triggers
BuildTrigger bt = p.getPublishersList().get(BuildTrigger.class);
if (bt != null) {
for (ListIterator<BuildTriggerConfig> btc = bt.getConfigs().listIterator(); btc.hasNext();) {
BuildTriggerConfig c = btc.next();
if (c.onDeleted(oldName)) {
if (c.onDeleted(p.getParent(), oldName)) {
changed = true;
if (c.getProjects().length() == 0){
btc.remove();
Expand All @@ -91,7 +99,7 @@ public void onDeleted(Item item) {
if (tb != null) {
for (ListIterator<BlockableBuildTriggerConfig> bbtc = tb.getConfigs().listIterator(); bbtc.hasNext();) {
BuildTriggerConfig c = bbtc.next();
if (c.onDeleted(oldName)) {
if (c.onDeleted(p.getParent(), oldName)) {
changed = true;
if (c.getProjects().length() == 0){
bbtc.remove();
Expand Down Expand Up @@ -135,7 +143,7 @@ private boolean renameInConditionalBuildStep(Project<?,?> p, String oldName, Str
final List<TriggerBuilder> containedBuilders = ConditionalBuildStepHelper.getContainedBuilders(p, TriggerBuilder.class);
for (TriggerBuilder triggerBuilder : containedBuilders) {
for (BuildTriggerConfig co : triggerBuilder.getConfigs()){
changed |= co.onJobRenamed(oldName, newName);
changed |= co.onJobRenamed(p.getParent(), oldName, newName);
}
}
return changed;
Expand All @@ -153,7 +161,7 @@ private boolean deleteInConditionalBuildStep(Project<?,?> p, String oldName) {
for (TriggerBuilder triggerBuilder : containedBuilders) {
for (ListIterator<BlockableBuildTriggerConfig> bbtc = triggerBuilder.getConfigs().listIterator(); bbtc.hasNext();) {
BuildTriggerConfig c = bbtc.next();
if (c.onDeleted(oldName)) {
if (c.onDeleted(p.getParent(), oldName)) {
changed = true;
if (c.getProjects().length() == 0){
bbtc.remove();
Expand Down
Expand Up @@ -27,17 +27,22 @@
import hudson.EnvVars;
import hudson.model.AbstractBuild;
import hudson.model.Cause.UserCause;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.Project;
import hudson.model.Result;
import hudson.model.Run;
import hudson.plugins.parameterizedtrigger.AbstractBuildParameterFactory;
import hudson.plugins.parameterizedtrigger.AbstractBuildParameters;
import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig;
import hudson.plugins.parameterizedtrigger.BlockingBehaviour;
import hudson.plugins.parameterizedtrigger.BuildInfoExporterAction;
import hudson.plugins.parameterizedtrigger.CounterBuildParameterFactory;
import hudson.plugins.parameterizedtrigger.CurrentBuildParameters;
import hudson.plugins.parameterizedtrigger.PredefinedBuildParameters;
import hudson.plugins.parameterizedtrigger.TriggerBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.MatcherAssert.*;
Expand Down Expand Up @@ -255,4 +260,58 @@ public void testNonBlocking() throws Exception {
assertThat(envVars, not(hasEntry("TRIGGERED_BUILD_RESULT_projectC_RUN_" + Integer.toString(expectedBuildNumberC), buildC1.getResult().toString())));

}

public void testProjectDeleted() throws Exception {
FreeStyleProject p1 = createFreeStyleProject();
FreeStyleProject p2 = createFreeStyleProject();

// Blocked build
p1.getBuildersList().add(new TriggerBuilder(new BlockableBuildTriggerConfig(
p2.getName(),
new BlockingBehaviour(
Result.FAILURE,
Result.UNSTABLE,
Result.FAILURE
),
Arrays.<AbstractBuildParameters>asList(
new PredefinedBuildParameters("test=test")
)
)));

FreeStyleBuild blockedBuild = p1.scheduleBuild2(0).get();
assertBuildStatusSuccess(blockedBuild);

// Unblocked build
p1.getBuildersList().clear();
p1.getBuildersList().add(new TriggerBuilder(new BlockableBuildTriggerConfig(
p2.getName(),
null,
Arrays.<AbstractBuildParameters>asList(
new PredefinedBuildParameters("test=test")
)
)));

FreeStyleBuild unblockedBuild = p1.scheduleBuild2(0).get();
assertBuildStatusSuccess(unblockedBuild);

waitUntilNoActivity();

assertEquals(1, blockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredBuilds().size());
assertEquals(p2.getBuildByNumber(1), blockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredBuilds().get(0));
assertEquals(0, blockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredProjects().size());

assertEquals(0, unblockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredBuilds().size());
assertEquals(1, unblockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredProjects().size());
assertEquals(p2, unblockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredProjects().get(0));

p2.delete();

assertEquals(1, blockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredBuilds().size());
assertNull(blockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredBuilds().get(0));
assertEquals(0, blockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredProjects().size());

assertEquals(0, unblockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredBuilds().size());
assertEquals(1, unblockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredProjects().size());
assertNull(unblockedBuild.getAction(BuildInfoExporterAction.class).getTriggeredProjects().get(0));
}
}

0 comments on commit 5534eb0

Please sign in to comment.