blob: 5cedfc94ad9bb462b4479d65d312210fffdd2c77 [file] [log] [blame]
# Copyright 2018 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import collections
# PatchInput describes the input to a `jiri patch` command. These are decoded from the
# list of JSON objects in a patchfile (see below).
#
# Properties:
# ref (str): The gerrit change ref (e.g refs/changes/aa/aabbcc/n)
# host (str): The code review host (e.g. fuchsia-review.googlesource.com)
# project (str): The patch project (e.g. garnet)
#
# Usage:
# These inputs are typically specified within a patchfile at the root of a project's
# directory. When checking out from a gerrit patchset (i.e. when running on CQ),
# CheckoutApi will patch any changes listed in this file.
PatchInput = collections.namedtuple("PatchInput", "ref host project")
class PatchFile(object):
"""A file used to patch one or more changes from unrelated projects into a
workspace.
The PatchFile should contain a list of JSON objects with the following structure:
[
{
"ref": "refs/changes/56/123456/3",
"host": "fuchsia-review.googlesource.com",
"project": "project",
}
]
"""
@staticmethod
def from_json(js):
"""Unmarshals a PatchFile from JSON."""
patch_inputs = []
for js_object in js:
patch_inputs.append(PatchInput(**js_object))
return PatchFile(patch_inputs)
def __init__(self, patch_inputs):
self.patch_inputs = patch_inputs
@property
def inputs(self):
return self.patch_inputs
def validate(self, gerrit_change):
"""Verifies the following about this PatchFile:
1. No input overwrites the Gerrit change that is currently being tested.
2. No two inputs patch over one another.
Returns:
A ValueError that should be raised as a StepFailure, if validation fails. Else
None.
"""
def create_key(project, host):
"""Produces a unique ID for a project + host combination."""
return "%s/%s" % (project, host)
# Maps validated PatchInput keys to their PatchInputs.
validated = {}
# The key for the original Gerrit change that is being tested.
gerrit_patch_key = create_key(gerrit_change.project, gerrit_change.host)
for patch_input in self.patch_inputs:
patch_key = create_key(patch_input.project, patch_input.host)
# User cannot use patches.json to overwrite the original gerrit change.
if patch_key == gerrit_patch_key:
return ValueError(
(
"This patch overwrites the original gerrit change: %s\n"
"Inline this patch into the change instead of specifying in patches.json"
)
% str(patch_input)
)
# User cannot patch multiple changes to the same project. Those changes should
# be tested locally.
if validated.get(patch_key, None):
return ValueError(
(
"Found patch that ovewrites a previous patch. These changes should be"
"tested together locally instead of through patches.json:\n"
"Original: %(original)s\nDuplicate: %(duplicate)s."
)
% dict(
original=str(validated[patch_key]), duplicate=str(patch_input),
)
)
validated[patch_key] = patch_input