blob: b7452f732fb354770423762310dc1c8eeddcba57 [file] [log] [blame]
# Copyright 2011-2012 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module that contains trybot patch pool code."""
import functools
import logging
from chromite.lib import config_lib
from chromite.lib import constants
from chromite.lib import gerrit
from chromite.lib import git
from chromite.lib import patch as cros_patch
def ChromiteFilter(patch):
"""Used with FilterFn to isolate patches to chromite."""
return patch.project == constants.CHROMITE_PROJECT
def ExtManifestFilter(patch):
"""Used with FilterFn to isolate patches to the external manifest."""
return patch.project == config_lib.GetSiteParams().MANIFEST_PROJECT
def IntManifestFilter(patch):
"""Used with FilterFn to isolate patches to the internal manifest."""
return patch.project == config_lib.GetSiteParams().MANIFEST_INT_PROJECT
def ManifestFilter(patch):
"""Used with FilterFn to isolate patches to the manifest."""
return ExtManifestFilter(patch) or IntManifestFilter(patch)
def BranchFilter(branch, patch):
"""Used with FilterFn to isolate patches based on a specific upstream."""
return patch.tracking_branch == branch
class TrybotPatchPool(object):
"""Represents patches specified by the user to test."""
def __init__(self, gerrit_patches=(), local_patches=(), remote_patches=()):
self.gerrit_patches = tuple(gerrit_patches)
self.local_patches = tuple(local_patches)
self.remote_patches = tuple(remote_patches)
def __bool__(self):
"""Returns True if the pool has any patches."""
return any(
[self.gerrit_patches, self.local_patches, self.remote_patches]
)
# Python 2 glue.
__nonzero__ = __bool__
def Filter(self, **kwargs):
"""Returns a new pool with only patches that match constraints.
Args:
**kwargs: constraints in the form of attr=value. I.e.,
project='chromiumos/chromite', tracking_branch='master'.
"""
def AttributeFilter(patch):
for key in kwargs:
if getattr(patch, key, object()) != kwargs[key]:
return False
return True
return self.FilterFn(AttributeFilter)
def FilterFn(self, filter_fn, negate=False):
"""Returns a new pool with only patches that match constraints.
Args:
filter_fn: Functor that accepts a 'patch' argument, and returns whether to
include the patch in the results.
negate: Return patches that don't pass the filter_fn.
"""
f = filter_fn
if negate:
f = lambda p: not filter_fn(p)
return self.__class__(
gerrit_patches=(x for x in self.gerrit_patches if f(x)),
local_patches=(x for x in self.local_patches if f(x)),
remote_patches=(x for x in self.remote_patches if f(x)),
)
def FilterManifest(self, negate=False):
"""Return a patch pool with only patches to the manifest."""
return self.FilterFn(ManifestFilter, negate=negate)
def FilterIntManifest(self, negate=False):
"""Return a patch pool with only patches to the internal manifest."""
return self.FilterFn(IntManifestFilter, negate=negate)
def FilterExtManifest(self, negate=False):
"""Return a patch pool with only patches to the external manifest."""
return self.FilterFn(ExtManifestFilter, negate=negate)
def FilterBranch(self, branch, negate=False):
"""Return a patch pool with only patches based on a particular branch."""
return self.FilterFn(
functools.partial(BranchFilter, branch), negate=negate
)
def __iter__(self):
for source in [
self.local_patches,
self.remote_patches,
self.gerrit_patches,
]:
for patch in source:
yield patch
@classmethod
def FromOptions(
cls,
gerrit_patches=None,
local_patches=None,
sourceroot=None,
remote_patches=None,
):
"""Generate patch objects from passed in options.
Args:
gerrit_patches: Gerrit ids that gerrit.GetGerritPatchInfo accepts.
local_patches: Local ids that cros_patch.PrepareLocalPatches accepts.
sourceroot: The source repository to look up |local_patches|.
remote_patches: Remote ids that cros_patch.PrepareRemotePatches accepts.
Returns:
A TrybotPatchPool object.
Raises:
gerrit.GerritException, cros_patch.PatchException
"""
if gerrit_patches:
gerrit_patches = gerrit.GetGerritPatchInfo(gerrit_patches)
for patch in gerrit_patches:
if patch.IsAlreadyMerged():
logging.warning("Patch %s has already been merged.", patch)
else:
gerrit_patches = ()
if local_patches:
manifest = git.ManifestCheckout.Cached(sourceroot)
local_patches = cros_patch.PrepareLocalPatches(
manifest, local_patches
)
else:
local_patches = ()
if remote_patches:
remote_patches = cros_patch.PrepareRemotePatches(remote_patches)
else:
remote_patches = ()
return cls(
gerrit_patches=gerrit_patches,
local_patches=local_patches,
remote_patches=remote_patches,
)