Support cross-repo-dependencies in independent pipelines
Changes supplied by the Depends-On header in git commit messages
will now additionally pull in changes from any project, on any
branch into independent pipelines. They are considered "non-live"
changes, that is, changes that should not have any tests run on
them.
To accomodate this, the internal structure of an independent
pipeline is now a dynamically adjusting list of ChangeQueue objects
where a new ChangeQueue is created for each change added to the
pipeline (and that ChangeQueue might contain non-live changes
enqueued ahead of the change we are interested in for the purpose
of stacking commits in the associated Zuul refs). This actually
more closely matches the visual and intuitive understanding of
independent pipelines.
Change-Id: I8ba0bc0918263f297666a50c607bca4f87c903b8
diff --git a/zuul/model.py b/zuul/model.py
index 3bba284..2a69b79 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -111,6 +111,9 @@
return queue
return None
+ def removeQueue(self, queue):
+ self.queues.remove(queue)
+
def getJobTree(self, project):
tree = self.job_trees.get(project)
return tree
@@ -148,6 +151,8 @@
return torun
def findJobsToRun(self, item):
+ if not item.live:
+ return []
tree = self.getJobTree(item.change.project)
if not tree:
return []
@@ -193,6 +198,8 @@
return False
def isHoldingFollowingChanges(self, item):
+ if not item.live:
+ return False
for job in self.getJobs(item.change):
if not job.hold_following_changes:
continue
@@ -256,7 +263,6 @@
j_queues.append(j_queue)
j_queue['heads'] = []
j_queue['window'] = queue.window
- j_queue['dependent'] = queue.dependent
j_changes = []
for e in queue.queue:
@@ -303,8 +309,8 @@
different projects; this is one of them. For instance, there may
a queue shared by interrelated projects foo and bar, and a second
queue for independent project baz. Pipelines have one or more
- PipelineQueues."""
- def __init__(self, pipeline, dependent=True, window=0, window_floor=1,
+ ChangeQueues."""
+ def __init__(self, pipeline, window=0, window_floor=1,
window_increase_type='linear', window_increase_factor=1,
window_decrease_type='exponential', window_decrease_factor=2):
self.pipeline = pipeline
@@ -314,7 +320,6 @@
self.projects = []
self._jobs = set()
self.queue = []
- self.dependent = dependent
self.window = window
self.window_floor = window_floor
self.window_increase_type = window_increase_type
@@ -348,14 +353,15 @@
self.name = self.assigned_name or self.generated_name
def enqueueChange(self, change):
- item = QueueItem(self.pipeline, change)
+ item = QueueItem(self, change)
self.enqueueItem(item)
item.enqueue_time = time.time()
return item
def enqueueItem(self, item):
item.pipeline = self.pipeline
- if self.dependent and self.queue:
+ item.queue = self
+ if self.queue:
item.item_ahead = self.queue[-1]
item.item_ahead.items_behind.append(item)
self.queue.append(item)
@@ -374,8 +380,6 @@
item.dequeue_time = time.time()
def moveItem(self, item, item_ahead):
- if not self.dependent:
- return False
if item.item_ahead == item_ahead:
return False
# Remove from current location
@@ -399,20 +403,20 @@
# TODO merge semantics
def isActionable(self, item):
- if self.dependent and self.window:
+ if self.window:
return item in self.queue[:self.window]
else:
return True
def increaseWindowSize(self):
- if self.dependent:
+ if self.window:
if self.window_increase_type == 'linear':
self.window += self.window_increase_factor
elif self.window_increase_type == 'exponential':
self.window *= self.window_increase_factor
def decreaseWindowSize(self):
- if self.dependent:
+ if self.window:
if self.window_decrease_type == 'linear':
self.window = max(
self.window_floor,
@@ -650,8 +654,9 @@
class QueueItem(object):
"""A changish inside of a Pipeline queue"""
- def __init__(self, pipeline, change):
- self.pipeline = pipeline
+ def __init__(self, queue, change):
+ self.pipeline = queue.pipeline
+ self.queue = queue
self.change = change # a changeish
self.build_sets = []
self.dequeued_needing_change = False
@@ -662,7 +667,8 @@
self.enqueue_time = None
self.dequeue_time = None
self.reported = False
- self.active = False
+ self.active = False # Whether an item is within an active window
+ self.live = True # Whether an item is intended to be processed at all
def __repr__(self):
if self.pipeline: