Skip to content

Commit

Permalink
Merge pull request #481 from ckreisl/extend-topic-association
Browse files Browse the repository at this point in the history
[JENKINS-70111] Provide options to prevent triggering jobs when Change status != NEW
  • Loading branch information
rsandell authored Dec 2, 2022
2 parents 6e95eb7 + d845f0c commit cd8f3ad
Show file tree
Hide file tree
Showing 11 changed files with 422 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.sonyericsson.hudson.plugins.gerrit.trigger.dependency.DependencyQueueTaskDispatcher;
import com.sonyericsson.hudson.plugins.gerrit.trigger.gerritnotifier.ToGerritRunListener;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.actions.GerritTriggerInformationAction;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data.TopicAssociation;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data.BuildCancellationPolicy;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data.GerritProject;
import com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data.GerritSlave;
Expand Down Expand Up @@ -73,6 +74,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.IOException;
Expand Down Expand Up @@ -134,7 +136,9 @@ public class GerritTrigger extends Trigger<Job> {
private Integer gerritBuildAbortedVerifiedValue;
private Integer gerritBuildAbortedCodeReviewValue;
private boolean silentMode;
private boolean enableTopicAssociation = Config.DEFAULT_ENABLE_TOPIC_ASSOCIATION;
@Deprecated
private transient boolean enableTopicAssociation = Config.DEFAULT_ENABLE_TOPIC_ASSOCIATION;
private TopicAssociation topicAssociation;
private String notificationLevel;
private boolean silentStartMode;
private boolean escapeQuotes;
Expand Down Expand Up @@ -951,24 +955,51 @@ private boolean isChangeInteresting(Change change, GerritProject project, Gerrit
}

/**
* Should we trigger on this topic?
* Should we consider this topic w.r.t Topic Association options?
*
* @param topic the topic
* @param project the configured gerrit project
* @param event the event
* @return true if we should.
*/
private boolean isTopicInteresting(Topic topic, GerritProject project) {
for (GerritServer server : PluginImpl.getServers_()) {
logger.trace("query topic {} from {} ({}:{})", topic, server.getName(), server.getHostName(),
server.getSshPort());
Map<Change, PatchSet> changes = topic.getChanges(server.getQueryHandler());
logger.trace("found {} changes with topic {} from {}", changes.size(), topic, server.getName());
for (Change change : changes.keySet()) {
if (isChangeInteresting(change, project, server.getQueryHandler())) {
return true;
}
private boolean isTopicInteresting(Topic topic, GerritProject project, ChangeBasedEvent event) {

if (topicAssociation == null) {
return false;
}

GerritServer server = PluginImpl.getServer_(event);

if (server == null) {
return false;
}

logger.trace("query topic {} from {} ({}:{})", topic, server.getName(), server.getHostName(),
server.getSshPort());

Map<Change, PatchSet> changes = topic.getChanges(server.getQueryHandler());

logger.trace("found {} changes with topic {} from {}", changes.size(), topic, server.getName());

for (Change change : changes.keySet()) {

if (change.equals(event.getChange())) {
continue;
}

if (!isChangeInteresting(change, project, server.getQueryHandler())) {
continue;
}

if (!topicAssociation.isInterestingChangeStatus(change)) {
continue;
}

logger.trace("topic: {} and change status: {} is interesting", topic, change.getStatus());

return true;
}

return false;
}

Expand Down Expand Up @@ -1002,20 +1033,33 @@ private boolean isChangeBasedEventInteresting(ChangeBasedEvent event, GerritProj
return true;
}

if (!isEnableTopicAssociation()) {
if (isTopicAssociationInteresting(event, project)) {
return true;
}

return false;
}

/**
* Check if the event is interesting for the Topic Association option.
*
* @param event The incoming ChangeBasedEvent.
* @param project The configured Gerrit project.
* @return true if the topic associated to the build is interesting otherwise false.
*/
private boolean isTopicAssociationInteresting(ChangeBasedEvent event, GerritProject project) {

if (topicAssociation == null) {
return false;
}

Change change = event.getChange();
Topic topic = change.getTopicObject();
if (topic == null) {
return false;
}

if (isTopicInteresting(topic, project)) {
return true;
}

return false;
return isTopicInteresting(topic, project, event);
}

/**
Expand Down Expand Up @@ -1848,22 +1892,49 @@ public void setSilentMode(boolean silentMode) {
}

/**
* Check if topic association is enabled.
* @return true if so.
* Enable or disable Topic Association option.
*
* @param enable true or false.
*/
@Deprecated
public void setEnableTopicAssociation(boolean enable) {
if (enable) {
topicAssociation = new TopicAssociation();
} else {
topicAssociation = null;
}
}

/**
* Check if topic association is enabled.
*
* @return true if so.
*/
@Deprecated
public boolean isEnableTopicAssociation() {
return enableTopicAssociation;
return topicAssociation != null;
}

/**
* Set topic association enabled.
* When topic association is on the job can be triggered by project not in its configuration
* as long as there are projects of interest in the same topic.
* @param enableTopicAssociation true if it should be enabled.
* DataBoundSetter for TopicAssociation.
* Used for jelly file.
*
* @param topicAssociation the TopicAssociation object.
*/
@DataBoundSetter
public void setEnableTopicAssociation(boolean enableTopicAssociation) {
this.enableTopicAssociation = enableTopicAssociation;
public void setTopicAssociation(final TopicAssociation topicAssociation) {
this.topicAssociation = topicAssociation;
}

/**
* Returns the assigned TopicAssociation object.
* Used for jelly file.
*
* @return TopicAssociation object
*/
@CheckForNull
public TopicAssociation getTopicAssociation() {
return topicAssociation;
}

/**
Expand Down Expand Up @@ -2105,6 +2176,13 @@ public Object readResolve() throws ObjectStreamException {
if (projectListIsReady == null) {
projectListIsReady = new CountDownLatch(0);
}

if (enableTopicAssociation) {
topicAssociation = new TopicAssociation();
} else {
topicAssociation = null;
}

return super.readResolve();
}
/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* The MIT License
*
* Copyright (c) 2022 Christoph Kreisl. All rights reserved.
*
* 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 com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data;

import com.sonymobile.tools.gerrit.gerritevents.dto.GerritChangeStatus;
import com.sonymobile.tools.gerrit.gerritevents.dto.attr.Change;
import hudson.Extension;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* Topic Association Option.
* If this option is enabled the job matching the topic configuration is triggered.
* Since changes assigned to a topic could be in an inappropriate state we check if changes
* in state NEW, MERGED or ABANDONED should be ignored.
*
* In general this shouldn't happen since changes can only be merged together.
* However, if someone adds a change to an already merged topic the jobs for the merged changes
* shouldn't be triggered again if not required.
*/
public class TopicAssociation extends AbstractDescribableImpl<TopicAssociation> {

private boolean ignoreNewChangeStatus;
private boolean ignoreMergedChangeStatus;
private boolean ignoreAbandonedChangeStatus;

/**
* Default data bound constructor.
*
* @param ignoreNewChangeStatus If changes with status NEW should be ignored
* @param ignoreMergedChangeStatus If changes with status MERGED should be ignored
* @param ignoreAbandonedChangeStatus If changes with status ABANDONED should be ignored
*/
@DataBoundConstructor
public TopicAssociation(final boolean ignoreNewChangeStatus,
final boolean ignoreMergedChangeStatus,
final boolean ignoreAbandonedChangeStatus) {
this.ignoreNewChangeStatus = ignoreNewChangeStatus;
this.ignoreMergedChangeStatus = ignoreMergedChangeStatus;
this.ignoreAbandonedChangeStatus = ignoreAbandonedChangeStatus;
}

/**
* Default constructor.
*/
public TopicAssociation() {
this.ignoreNewChangeStatus = false;
this.ignoreMergedChangeStatus = false;
this.ignoreAbandonedChangeStatus = false;
}

/**
* Returns true if a change in state NEW should be ignored otherwise false.
*
* @return true if it should be ignored otherwise false
*/
public boolean isIgnoreNewChangeStatus() {
return ignoreNewChangeStatus;
}

/**
* Enable or disable ignoring changes with status NEW.
*
* @param ignoreNewChangeStatus true or false.
*/
public void setIgnoreNewChangeStatus(boolean ignoreNewChangeStatus) {
this.ignoreNewChangeStatus = ignoreNewChangeStatus;
}

/**
* Returns true if a change in state NEW should be ignored otherwise false.
* Used for jelly file.
*
* @return true if it should be ignored otherwise false
*/
public boolean isIgnoreMergedChangeStatus() {
return ignoreMergedChangeStatus;
}

/**
* Enable or disable ignoring changes with status MERGED.
*
* @param ignoreMergedChangeStatus true or false.
*/
public void setIgnoreMergedChangeStatus(boolean ignoreMergedChangeStatus) {
this.ignoreMergedChangeStatus = ignoreMergedChangeStatus;
}

/**
* Returns true if a change in state NEW should be ignored otherwise false.
* Used for jelly file.
*
* @return true if it should be ignored otherwise false
*/
public boolean isIgnoreAbandonedChangeStatus() {
return ignoreAbandonedChangeStatus;
}

/**
* Enable or disable ignoring changes with status ABANDONED.
*
* @param ignoreAbandonedChangeStatus true or false.
*/
public void setIgnoreAbandonedChangeStatus(boolean ignoreAbandonedChangeStatus) {
this.ignoreAbandonedChangeStatus = ignoreAbandonedChangeStatus;
}

/**
* Checks if the change state is interesting.
*
* @param c the change.
* @return true if the change is interesting otherwise false.
*/
public boolean isInterestingChangeStatus(final Change c) {
boolean isNewChange = c.getStatus().equals(GerritChangeStatus.NEW);
boolean isMergedChange = c.getStatus().equals(GerritChangeStatus.MERGED);
boolean isAbandonedChange = c.getStatus().equals(GerritChangeStatus.ABANDONED);

if (isNewChange && ignoreNewChangeStatus) {
return false;
}

if (isMergedChange && ignoreMergedChangeStatus) {
return false;
}

if (isAbandonedChange && ignoreAbandonedChangeStatus) {
return false;
}

return true;
}

/**
* The Descriptor for the Topic Association.
*/
@Extension
public static class DescriptorImpl extends Descriptor<TopicAssociation> {
@Override
public String getDisplayName() {
return "Topic Association";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,11 @@
<f:checkbox name="silentMode"
checked="${it.silentMode}"/>
</f:entry>
<f:entry title="${%Enable Topic Association}"
field="enableTopicAssociation"
help="/plugin/gerrit-trigger/trigger/help-EnableTopicAssociation.html">
<f:checkbox name="enableTopicAssociation"
checked="${it.enableTopicAssociation}"/>
</f:entry>

<f:optionalProperty field="topicAssociation"
title="${%Topic Association}"
help="/plugin/gerrit-trigger/trigger/help-TopicAssociation.html" />

<f:entry title="${%Trigger on}"
help="/plugin/gerrit-trigger/trigger/help-GerritEventType.html">
<f:hetero-list descriptors="${descriptor.getGerritEventDescriptors()}" items="${instance.triggerOnEvents}" name="triggerOnEvents" hasHeader="true"/>
Expand Down
Loading

0 comments on commit cd8f3ad

Please sign in to comment.