def trigger_hook(hook_group_id, hook_id, hook_payload): hooks = Hooks({'rootUrl': get_root_url(True)}) response = hooks.triggerHook(hook_group_id, hook_id, hook_payload) logger.info('Task seen here: {}/tasks/{}'.format( get_root_url(os.environ.get('TASKCLUSTER_PROXY_URL')), response['status']['taskId']))
def __init__(self, publish=False, risk_analysis_reviewers=[], community_config=None, *args, **kwargs): super().__init__(*args, **kwargs) self.publish = publish logger.info("Phabricator publication is {}".format( self.publish and "enabled" or "disabled")) # Setup Taskcluster community hooks for risk analysis if community_config is not None: self.community_hooks = Hooks({ "rootUrl": "https://community-tc.services.mozilla.com", "credentials": { "clientId": community_config["client_id"], "accessToken": community_config["access_token"], }, }) logger.info("Risk analysis trigger is enabled") else: self.community_hooks = None logger.info( "No taskcluster_community in secret, risk analysis is disabled" ) self.risk_analysis_reviewers = risk_analysis_reviewers
def trigger_hook(hook_group_id, hook_id, hook_payload): hooks = Hooks({"rootUrl": get_root_url(True)}) response = hooks.triggerHook(hook_group_id, hook_id, hook_payload) logger.info("Task seen here: {}/tasks/{}".format( get_root_url(os.environ.get("TASKCLUSTER_PROXY_URL")), response["status"]["taskId"], ))
def __init__(self, publish=False, risk_analysis_reviewers=[], community_config=None, user_blacklist=[], *args, **kwargs): super().__init__(*args, **kwargs) self.publish = publish logger.info("Phabricator publication is {}".format( self.publish and "enabled" or "disabled")) # Setup Taskcluster community hooks for risk analysis if community_config is not None: self.community_hooks = Hooks({ "rootUrl": "https://community-tc.services.mozilla.com", "credentials": { "clientId": community_config["client_id"], "accessToken": community_config["access_token"], }, }) logger.info("Risk analysis trigger is enabled") else: self.community_hooks = None logger.info( "No taskcluster_community in secret, risk analysis is disabled" ) self.risk_analysis_reviewers = risk_analysis_reviewers # Load the blacklisted users if user_blacklist: self.user_blacklist = { user["phid"]: user["fields"]["username"] for user in self.api.search_users( constraints={"usernames": user_blacklist}) } logger.info("Blacklisted users", names=self.user_blacklist.values()) else: self.user_blacklist = {} logger.info("No blacklisted user")
class CodeReview(PhabricatorActions): """ Code review workflow, receiving build notifications from HarborMaster and pushing on Try repositories """ def __init__(self, publish=False, risk_analysis_reviewers=[], community_config=None, *args, **kwargs): super().__init__(*args, **kwargs) self.publish = publish logger.info("Phabricator publication is {}".format( self.publish and "enabled" or "disabled")) # Setup Taskcluster community hooks for risk analysis if community_config is not None: self.community_hooks = Hooks({ "rootUrl": "https://community-tc.services.mozilla.com", "credentials": { "clientId": community_config["client_id"], "accessToken": community_config["access_token"], }, }) logger.info("Risk analysis trigger is enabled") else: self.community_hooks = None logger.info( "No taskcluster_community in secret, risk analysis is disabled" ) self.risk_analysis_reviewers = risk_analysis_reviewers def register(self, bus): self.bus = bus self.bus.add_queue(QUEUE_PHABRICATOR_RESULTS) def get_repositories(self, repositories, cache_root): """ Configure repositories, and index them by phid """ repositories = { phab_repo["phid"]: Repository(conf, cache_root) for phab_repo in self.api.list_repositories() for conf in repositories if phab_repo["fields"]["name"] == conf["name"] } assert len(repositories) > 0, "No repositories configured" logger.info("Configured repositories", names=[r.name for r in repositories.values()]) return repositories async def run(self): """ Code review workflow to load all necessary information from Phabricator builds received from the webserver """ while True: # Receive build from webserver build = await self.bus.receive(QUEUE_WEB_BUILDS) assert build is not None, "Invalid payload" assert isinstance(build, PhabricatorBuild) # Update its state self.update_state(build) if build.state == PhabricatorBuildState.Public: # When the build is public, load needed details try: self.load_patches_stack(build) logger.info("Loaded stack of patches", build=str(build)) self.load_reviewers(build) logger.info("Loaded reviewers", build=str(build)) except Exception as e: logger.warning("Failed to load build details", build=str(build), error=str(e)) continue # Then send the build toward next stage logger.info("Send build to Mercurial", build=str(build)) await self.bus.send(QUEUE_MERCURIAL, build) # Report public bug as 'working' (in progress) await self.bus.send(QUEUE_PHABRICATOR_RESULTS, ("work", build, {})) # Start risk analysis await self.start_risk_analysis(build) elif build.state == PhabricatorBuildState.Queued: # Requeue when nothing changed for now await self.bus.send(QUEUE_WEB_BUILDS, build) def publish_results(self, payload): assert self.publish is True, "Publication disabled" mode, build, extras = payload logger.debug("Publishing a Phabricator build update", mode=mode, build=build) if mode == "fail:general": failure = UnitResult( namespace="code-review", name="general", result=UnitResultState.Broken, details= "WARNING: An error occurred in the code review bot.\n\n```{}```" .format(extras["message"]), format="remarkup", duration=extras.get("duration", 0), ) self.api.update_build_target(build.target_phid, BuildState.Fail, unit=[failure]) elif mode == "fail:mercurial": failure = UnitResult( namespace="code-review", name="mercurial", result=UnitResultState.Fail, details= "WARNING: The code review bot failed to apply your patch.\n\n```{}```" .format(extras["message"]), format="remarkup", duration=extras.get("duration", 0), ) self.api.update_build_target(build.target_phid, BuildState.Fail, unit=[failure]) elif mode == "success": self.api.create_harbormaster_uri( build.target_phid, "treeherder", "Treeherder Jobs", extras["treeherder_url"], ) elif mode == "work": self.api.update_build_target(build.target_phid, BuildState.Work) logger.info("Published public build as working", build=str(build)) else: logger.warning("Unsupported publication", mode=mode, build=build) return True async def start_risk_analysis(self, build): """ Run risk analysis by triggering a Taskcluster hook """ assert isinstance(build, PhabricatorBuild) assert build.state == PhabricatorBuildState.Public try: if self.should_run_risk_analysis(build): task = self.community_hooks.triggerHook( "project-relman", "bugbug-classify-patch", {"DIFF_ID": build.diff_id}, ) task_id = task["status"]["taskId"] logger.info("Triggered a new risk analysis task", id=task_id) # Send task to monitoring await self.bus.send( QUEUE_MONITORING, ("project-relman", "bugbug-classify-patch", task_id), ) except Exception as e: logger.error("Failed to trigger risk analysis task", error=str(e)) def should_run_risk_analysis(self, build): """ Check if we should trigger a risk analysis for this revision: * when the revision is being reviewed by one of some specific reviewers """ if self.community_hooks is None: return False usernames = set( [reviewer["fields"]["username"] for reviewer in build.reviewers]) return len(usernames.intersection(self.risk_analysis_reviewers)) > 0