| # Copyright 2018 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Module to manage local build state.""" |
| |
| import json |
| import logging |
| from typing import Any |
| |
| from chromite.lib import constants |
| from chromite.utils import pformat |
| |
| |
| class BuildSummary: |
| """Summarizes a build state without any external references. |
| |
| This is basically a dictionary that can convert itself back and forth from |
| JSON, but it provides the convenience of attribute-based access instead of |
| carrying around dictionary key constants. |
| |
| Attributes: |
| build_number: The build number of this build, as passed in with |
| --buildnumber, or 0 if --buildnumber wasn't passed. |
| buildbucket_id: The buildbucket id of this build, as passed in with |
| --buildbucket-id, or 0 if --buildbucket-id wasn't passed. |
| master_build_id: The CIDB id of the associated master build if there is |
| one, or 0 if there isn't one. |
| status: One of the status constants from |
| chromite.lib.constants.BUILDER_ALL_STATUSES |
| buildroot_layout: Version of the buildroot layout. |
| branch: Name of the branch this repository is associated with. |
| distfiles_ts: Float unix timestamp recording the last time the distfiles |
| cache was cleaned. |
| """ |
| |
| # List of attributes that should be saved and restored to represent |
| # this object. We use an explicit list instead of vars() so that future |
| # additions can be handled explicitly. |
| _PERSIST_ATTRIBUTES = ( |
| "build_number", |
| "buildbucket_id", |
| "master_build_id", |
| "status", |
| "buildroot_layout", |
| "branch", |
| "distfiles_ts", |
| ) |
| |
| def __init__( |
| self, |
| build_number=0, |
| buildbucket_id=0, |
| master_build_id=0, |
| status=constants.BUILDER_STATUS_MISSING, |
| buildroot_layout=0, |
| branch="", |
| distfiles_ts=None, |
| ) -> None: |
| self.build_number = build_number |
| self.buildbucket_id = buildbucket_id |
| self.master_build_id = master_build_id |
| self.status = status |
| self.buildroot_layout = buildroot_layout |
| self.branch = branch |
| self.distfiles_ts = distfiles_ts |
| |
| def __eq__(self, other: Any) -> bool: |
| for a in self._PERSIST_ATTRIBUTES: |
| if hasattr(other, a) != hasattr(self, a): |
| return False |
| if getattr(other, a) != getattr(self, a): |
| return False |
| return True |
| |
| def __repr__(self) -> str: |
| return "BuildSummary(%s)" % self.to_json() |
| |
| def from_json(self, raw_json) -> None: |
| """Merge the state encoded in |raw_json| into this object. |
| |
| Unknown keys will be ignored (with a warning). Values for missing keys |
| will remain unchanged. |
| |
| Args: |
| raw_json: String containing valid JSON representing a BuildSummary. |
| |
| Raises: |
| ValueError: |raw_json| is not valid JSON. |
| """ |
| new_state = json.loads(raw_json) |
| for key, val in new_state.items(): |
| if key in self._PERSIST_ATTRIBUTES: |
| setattr(self, key, val) |
| else: |
| logging.warning('Ignoring unrecognized JSON key "%s"', key) |
| |
| if self.status not in constants.BUILDER_ALL_STATUSES: |
| logging.warning('Ingoring unknown build status "%s"', self.status) |
| self.status = constants.BUILDER_STATUS_MISSING |
| |
| def to_json(self): |
| """Serialize this object to JSON. |
| |
| Attributes that have an empty/zero value are omitted from the output. |
| The output of this function can be passed to from_json() to get back |
| another BuildSummary with the same values. |
| |
| Returns: |
| A string containing a JSON-encoded representation of this object. |
| """ |
| state = {} |
| for a in self._PERSIST_ATTRIBUTES: |
| val = getattr(self, a) |
| if val: |
| state[a] = getattr(self, a) |
| return pformat.json(state, compact=True) |
| |
| def is_valid(self): |
| """Indicate whether this object has a valid status.""" |
| return self.status != constants.BUILDER_STATUS_MISSING |
| |
| def build_description(self): |
| """Get a human-readable description of which build id is set. |
| |
| If multiple fields are set, buildbucket is preferred. The result can be |
| used for logging or display, but should not be parsed for database |
| lookups. |
| |
| Returns: |
| String describing which build id is set, or "local build" if none |
| are set. |
| """ |
| if self.buildbucket_id: |
| return "buildbucket_id=%s" % self.buildbucket_id |
| if self.build_number: |
| return "build_number=%s" % self.build_number |
| return "local build" |