def save_build_config(self): """Save config in the build object.""" pk = self.build['id'] config = self.config.as_dict() api_v2.build(pk).patch({ 'config': config, }) self.build['config'] = config
def update_build(self, state=None): """Record a build by hitting the API This step is skipped if we aren't recording the build, or if we don't want to record successful builds yet (if we are running setup commands for the build) """ if not self.record: return None self.build['project'] = self.project.pk self.build['version'] = self.version.pk self.build['builder'] = socket.gethostname() self.build['state'] = state if self.done: self.build['success'] = self.successful # TODO drop exit_code and provide a more meaningful UX for error # reporting if self.failure and isinstance(self.failure, BuildEnvironmentException): self.build['exit_code'] = self.failure.status_code elif self.commands: self.build['exit_code'] = max( [cmd.exit_code for cmd in self.commands]) self.build['setup'] = self.build['setup_error'] = "" self.build['output'] = self.build['error'] = "" if self.start_time: build_length = (datetime.utcnow() - self.start_time) self.build['length'] = int(build_length.total_seconds()) if self.failure is not None: # Only surface the error message if it was a # BuildEnvironmentException or BuildEnvironmentWarning if isinstance( self.failure, (BuildEnvironmentException, BuildEnvironmentWarning)): self.build['error'] = str(self.failure) else: self.build['error'] = ugettext_noop( "An unexpected error occurred") # Attempt to stop unicode errors on build reporting for key, val in list(self.build.items()): if isinstance(val, six.binary_type): self.build[key] = val.decode('utf-8', 'ignore') try: api_v2.build(self.build['id']).put(self.build) except HttpClientError as e: log.error("Unable to post a new build: %s", e.content) except Exception: log.error("Unknown build exception", exc_info=True)
def update_build(self, state=None): """Record a build by hitting the API This step is skipped if we aren't recording the build, or if we don't want to record successful builds yet (if we are running setup commands for the build) """ if not self.record: return None self.build['project'] = self.project.pk self.build['version'] = self.version.pk self.build['builder'] = socket.gethostname() self.build['state'] = state if self.done: self.build['success'] = self.successful # TODO drop exit_code and provide a more meaningful UX for error # reporting if self.failure and isinstance(self.failure, BuildEnvironmentException): self.build['exit_code'] = self.failure.status_code elif self.commands: self.build['exit_code'] = max([cmd.exit_code for cmd in self.commands]) self.build['setup'] = self.build['setup_error'] = "" self.build['output'] = self.build['error'] = "" if self.start_time: build_length = (datetime.utcnow() - self.start_time) self.build['length'] = int(build_length.total_seconds()) if self.failure is not None: # Only surface the error message if it was a # BuildEnvironmentException or BuildEnvironmentWarning if isinstance(self.failure, (BuildEnvironmentException, BuildEnvironmentWarning)): self.build['error'] = str(self.failure) else: self.build['error'] = ugettext_noop( "An unexpected error occurred") # Attempt to stop unicode errors on build reporting for key, val in list(self.build.items()): if isinstance(val, six.binary_type): self.build[key] = val.decode('utf-8', 'ignore') try: api_v2.build(self.build['id']).put(self.build) except HttpClientError as e: log.error("Unable to post a new build: %s", e.content) except Exception: log.error("Unknown build exception", exc_info=True)
def get_build(build_pk): """ Retrieve build object from API :param build_pk: Build primary key """ build = {} if build_pk: build = api_v2.build(build_pk).get() return dict((key, val) for (key, val) in build.items() if key not in ['project', 'version', 'resource_uri', 'absolute_uri'])
def run(self, pk, version_pk=None, build_pk=None, record=True, docker=False, search=True, force=False, localmedia=True, **__): """ Run a documentation build. This is fully wrapped in exception handling to account for a number of failure cases. """ try: self.project = self.get_project(pk) self.version = self.get_version(self.project, version_pk) self.build = self.get_build(build_pk) self.build_search = search self.build_localmedia = localmedia self.build_force = force self.config = None setup_successful = self.run_setup(record=record) if setup_successful: self.run_build(record=record, docker=docker) failure = self.setup_env.failure or self.build_env.failure except Exception as e: # noqa log.exception( 'An unhandled exception was raised outside the build environment', extra={'tags': { 'build': build_pk }}) failure = _( 'Unknown error encountered. ' 'Please include the build id ({build_id}) in any bug reports.'. format(build_id=build_pk)) # **Always** report build status. # This can still fail if the API Is totally down, but should catch more failures result = {} build_updates = {'state': BUILD_STATE_FINISHED} build_data = {} if hasattr(self, 'build'): build_data.update(self.build) if failure: build_updates['success'] = False build_updates['error'] = failure build_data.update(build_updates) result = api_v2.build(build_pk).patch(build_updates) return result
def update_build(self, state=None): """Record a build by hitting the API This step is skipped if we aren't recording the build, or if we don't want to record successful builds yet (if we are running setup commands for the build) """ if not self.record or (state == BUILD_STATE_FINISHED and not self.report_build_success): return None self.build['project'] = self.project.pk self.build['version'] = self.version.pk self.build['builder'] = socket.gethostname() self.build['state'] = state if self.done: self.build['success'] = self.successful # TODO drop exit_code and provide a more meaningful UX for error # reporting if self.failure and isinstance(self.failure, BuildEnvironmentException): self.build['exit_code'] = self.failure.status_code elif len(self.commands) > 0: self.build['exit_code'] = max( [cmd.exit_code for cmd in self.commands]) self.build['setup'] = self.build['setup_error'] = "" self.build['output'] = self.build['error'] = "" if self.start_time: build_length = (datetime.utcnow() - self.start_time) self.build['length'] = build_length.total_seconds() if self.failure is not None: self.build['error'] = str(self.failure) # Attempt to stop unicode errors on build reporting for key, val in self.build.items(): if isinstance(val, basestring): self.build[key] = val.decode('utf-8', 'ignore') try: resp = api_v2.build(self.build['id']).put(self.build) except Exception: log.error("Unable to post a new build", exc_info=True)
def get_build(build_pk): """ Retrieve build object from API. :param build_pk: Build primary key """ build = {} if build_pk: build = api_v2.build(build_pk).get() private_keys = [ 'project', 'version', 'resource_uri', 'absolute_uri', ] return { key: val for key, val in build.items() if key not in private_keys }
def update_build(self, state=None): """Record a build by hitting the API This step is skipped if we aren't recording the build, or if we don't want to record successful builds yet (if we are running setup commands for the build) """ if not self.record or (state == BUILD_STATE_FINISHED and not self.report_build_success): return None self.build['project'] = self.project.pk self.build['version'] = self.version.pk self.build['builder'] = socket.gethostname() self.build['state'] = state if self.done: self.build['success'] = self.successful # TODO drop exit_code and provide a more meaningful UX for error # reporting if self.failure and isinstance(self.failure, BuildEnvironmentException): self.build['exit_code'] = self.failure.status_code elif len(self.commands) > 0: self.build['exit_code'] = max([cmd.exit_code for cmd in self.commands]) self.build['setup'] = self.build['setup_error'] = "" self.build['output'] = self.build['error'] = "" if self.start_time: build_length = (datetime.utcnow() - self.start_time) self.build['length'] = build_length.total_seconds() if self.failure is not None: self.build['error'] = str(self.failure) # Attempt to stop unicode errors on build reporting for key, val in self.build.items(): if isinstance(val, basestring): self.build[key] = val.decode('utf-8', 'ignore') try: resp = api_v2.build(self.build['id']).put(self.build) except Exception: log.error("Unable to post a new build", exc_info=True)
def run(self, pk, version_pk=None, build_pk=None, record=True, docker=False, search=True, force=False, localmedia=True, **__): """ Run a documentation build. This is fully wrapped in exception handling to account for a number of failure cases. """ try: self.project = self.get_project(pk) self.version = self.get_version(self.project, version_pk) self.build = self.get_build(build_pk) self.build_search = search self.build_localmedia = localmedia self.build_force = force self.config = None setup_successful = self.run_setup(record=record) if setup_successful: self.run_build(record=record, docker=docker) failure = self.setup_env.failure or self.build_env.failure except Exception as e: # noqa log.exception( 'An unhandled exception was raised outside the build environment', extra={'tags': {'build': build_pk}} ) failure = _('Unknown error encountered. ' 'Please include the build id ({build_id}) in any bug reports.'.format( build_id=build_pk )) # **Always** report build status. # This can still fail if the API Is totally down, but should catch more failures result = {} build_updates = {'state': BUILD_STATE_FINISHED} build_data = {} if hasattr(self, 'build'): build_data.update(self.build) if failure: build_updates['success'] = False build_updates['error'] = failure build_data.update(build_updates) result = api_v2.build(build_pk).patch(build_updates) return result
def update_build(self, state=None): """ Record a build by hitting the API. This step is skipped if we aren't recording the build. To avoid recording successful builds yet (for instance, running setup commands for the build), set the ``update_on_success`` argument to False on environment instantiation. If there was an error on the build, update the build regardless of whether ``update_on_success`` is ``True`` or not. """ if not self.record: return None self.build['project'] = self.project.pk self.build['version'] = self.version.pk self.build['builder'] = socket.gethostname() self.build['state'] = state if self.done: self.build['success'] = self.successful # TODO drop exit_code and provide a more meaningful UX for error # reporting if self.failure and isinstance(self.failure, BuildEnvironmentException): self.build['exit_code'] = self.failure.status_code elif self.commands: self.build['exit_code'] = max( [cmd.exit_code for cmd in self.commands]) self.build['setup'] = self.build['setup_error'] = "" self.build['output'] = self.build['error'] = "" if self.start_time: build_length = (datetime.utcnow() - self.start_time) self.build['length'] = int(build_length.total_seconds()) if self.failure is not None: # Surface a generic error if the class is not a # BuildEnvironmentError if not isinstance( self.failure, (BuildEnvironmentException, BuildEnvironmentWarning)): log.error('Build failed with unhandled exception: %s', str(self.failure), extra={ 'stack': True, 'tags': { 'build': self.build['id'] }, }) self.failure = BuildEnvironmentError( BuildEnvironmentError.GENERIC_WITH_BUILD_ID.format( build_id=self.build['id'], )) self.build['error'] = str(self.failure) # Attempt to stop unicode errors on build reporting for key, val in list(self.build.items()): if isinstance(val, six.binary_type): self.build[key] = val.decode('utf-8', 'ignore') # We are selective about when we update the build object here update_build = ( # Build isn't done yet, we unconditionally update in this state not self.done # Build is done, but isn't successful, always update or (self.done and not self.successful) # Otherwise, are we explicitly to not update? or self.update_on_success) if update_build: try: api_v2.build(self.build['id']).put(self.build) except HttpClientError as e: log.exception( "Unable to update build: id=%d", self.build['id'], ) except Exception: log.exception("Unknown build exception")
def update_build(self, state=None): """ Record a build by hitting the API. This step is skipped if we aren't recording the build. To avoid recording successful builds yet (for instance, running setup commands for the build), set the ``update_on_success`` argument to False on environment instantiation. If there was an error on the build, update the build regardless of whether ``update_on_success`` is ``True`` or not. """ if not self.record: return None self.build['project'] = self.project.pk self.build['version'] = self.version.pk self.build['builder'] = socket.gethostname() self.build['state'] = state if self.done: self.build['success'] = self.successful # TODO drop exit_code and provide a more meaningful UX for error # reporting if self.failure and isinstance(self.failure, BuildEnvironmentException): self.build['exit_code'] = self.failure.status_code elif self.commands: self.build['exit_code'] = max([cmd.exit_code for cmd in self.commands]) self.build['setup'] = self.build['setup_error'] = "" self.build['output'] = self.build['error'] = "" if self.start_time: build_length = (datetime.utcnow() - self.start_time) self.build['length'] = int(build_length.total_seconds()) if self.failure is not None: # Surface a generic error if the class is not a # BuildEnvironmentError if not isinstance(self.failure, (BuildEnvironmentException, BuildEnvironmentWarning)): log.error( 'Build failed with unhandled exception: %s', str(self.failure), extra={ 'stack': True, 'tags': {'build': self.build['id']}, } ) self.failure = BuildEnvironmentError( BuildEnvironmentError.GENERIC_WITH_BUILD_ID.format( build_id=self.build['id'], ) ) self.build['error'] = str(self.failure) # Attempt to stop unicode errors on build reporting for key, val in list(self.build.items()): if isinstance(val, six.binary_type): self.build[key] = val.decode('utf-8', 'ignore') # We are selective about when we update the build object here update_build = ( # Build isn't done yet, we unconditionally update in this state not self.done # Build is done, but isn't successful, always update or (self.done and not self.successful) # Otherwise, are we explicitly to not update? or self.update_on_success ) if update_build: try: api_v2.build(self.build['id']).put(self.build) except HttpClientError as e: log.exception( "Unable to update build: id=%d", self.build['id'], ) except Exception: log.exception("Unknown build exception")