class GitlabTests(unittest.TestCase): def setUp(self): self.token = os.environ.get("GITLAB_TOKEN") self.user = os.environ.get("GITLAB_USER") test_name = self.id() or "all" persistent_data_file = os.path.join( PERSISTENT_DATA_PREFIX, f"test_gitlab_data_{test_name}.yaml" ) PersistentObjectStorage().storage_file = persistent_data_file if PersistentObjectStorage().is_write_mode and ( not self.user or not self.token ): raise EnvironmentError("please set GITLAB_TOKEN GITLAB_USER env variables") else: self.token = "some_token" self.service = GitlabService( token=self.token, url="https://gitlab.gnome.org", ssl_verify=True ) self.project = self.service.get_project( repo="testing-ogr-repo", namespace="lbarcziova" ) def tearDown(self): PersistentObjectStorage().dump()
class GitlabTests(unittest.TestCase): def setUp(self): self.token = os.environ.get("GITLAB_TOKEN") test_name = self.id() or "all" persistent_data_file = os.path.join( PERSISTENT_DATA_PREFIX, f"test_gitlab_data_{test_name}.yaml" ) PersistentObjectStorage().storage_file = persistent_data_file if PersistentObjectStorage().is_write_mode and not self.token: raise EnvironmentError("please set GITLAB_TOKEN env variables") elif not self.token: self.token = "some_token" self.service = GitlabService( token=self.token, instance_url="https://gitlab.com", ssl_verify=True ) self.project = self.service.get_project( repo="ogr-tests", namespace="packit-service" ) def tearDown(self): PersistentObjectStorage().dump()
def create_ogr_service( service_type: str, service_url: Optional[str] = None, token: Optional[str] = None, github_app_id: Optional[str] = None, github_private_key_path: Optional[str] = None, ): """Create a new OGR service for interacting with remote GitForges.""" service_type = service_type.upper() if service_type == "GITHUB": ogr_service = GithubService( token=token, github_app_id=os.getenv("GITHUB_APP_ID"), github_private_key_path=os.getenv("GITHUB_PRIVATE_KEY_PATH"), ) elif service_type == "GITLAB": ogr_service = GitlabService(token=token, instance_url=service_url) elif service_type == "PAGURE": ogr_service = PagureService( token=token, instance_url=service_url, ) else: raise NotImplementedError(f"Kebechet cannot act on {service_type}") return ogr_service
def setUp(self): super().setUp() self.token = os.environ.get("GITLAB_TOKEN") if PersistentObjectStorage().mode == StorageMode.write and not self.token: raise EnvironmentError( "You are in Requre write mode, please set GITLAB_TOKEN env variables" ) elif not self.token: self.token = "some_token" self.service = GitlabService( token=self.token, instance_url="https://gitlab.com", ssl_verify=True ) self.project = self.service.get_project( repo="ogr-tests", namespace="packit-service" )
def mock_config(self): service_config = ServiceConfig() service_config.services = { GithubService(token="token"), GitlabService(token="token"), } service_config.dry_run = False service_config.github_requests_log_path = "/path" ServiceConfig.service_config = service_config
def ogr_service(cls) -> BaseGitService: if cls.SERVICE_NAME == "GITHUB": return GithubService( token=cls.AUTH_TOKEN, github_app_id=cls.GITHUB_APP_ID, github_app_private_key_path=cls.GITHUB_APP_PRIVATE_KEY_PATH, ) elif cls.SERVICE_NAME == "GITLAB": return GitlabService(token=cls.AUTH_TOKEN, instance_url=cls.SERVICE_URL) elif cls.SERVICE_NAME == "PAGURE": return PagureService(token=cls.AUTH_TOKEN, instance_url=cls.SERVICE_URL) else: raise NotImplementedError( f"Varangian cannot run on {cls.SERVICE_NAME} git services.")
def _ogr_service_from_dict(service_dict: Dict[str, str]) -> BaseGitService: if service_dict["service_name"] == "GITHUB": return GithubService( token=service_dict.get("auth_token"), github_app_id=service_dict.get("github_app_id"), github_app_private_key_path=service_dict.get( "github_app_private_key_path"), ) elif service_dict["service_name"] == "GITLAB": return GitlabService(token=service_dict.get("auth_token"), instance_url=service_dict.get("service_url")) elif service_dict["service_name"] == "PAGURE": return PagureService(token=service_dict.get("auth_token"), instance_url=service_dict.get("service_url")) else: raise NotImplementedError( f"Varangian cannot run on {service_dict['service_name']} git services." )
def _init_helper(self): """Handle the initialization or reinitialization of OGR object.""" if self.service_type == ServiceType.GITHUB: if self.service_url: self.service = GithubService(self.token, instance_url=self.service_url) else: self.service = GithubService(self.token) self.repository = self.service.get_project( repo=self.repo, namespace=self.namespace) elif self.service_type == ServiceType.GITLAB: if self.service_url: self.service = GitlabService(self.token, instance_url=self.service_url) else: self.service = GitlabService(self.token) self.repository = self.service.get_project( repo=self.repo, namespace=self.namespace) else: raise NotImplementedError
def service(self): if not self._service: self._service = GitlabService( token=self.token, instance_url="https://gitlab.com", ssl_verify=True ) return self._service
class SourceManagement: """Abstract source code management services like GitHub and GitLab.""" def __init__( self, service_type: ServiceType, service_url: str, slug: str, token: Optional[str], installation: bool = True, ): """Initialize source code management tools abstraction. Note that we are using OGR for calls. OGR keeps URL to services in its global context per GitHub/GitLab. This is global context is initialized in the manager with a hope to fix this behavior for our needs. """ self.service_type = service_type self.service_url = service_url self.slug = slug self.service_url = service_url self.token = token self.namespace, self.repo = slug.rsplit("/", 1) self.installation = installation # token expires after 10 mins self.token_expire_time = datetime.datetime.now() + datetime.timedelta( minutes=9, seconds=30) self.github_auth_obj = None if not installation and not token: raise ValueError( "Token nor Installation ID found during initialization.") if installation: self.github_auth_obj = GithubAuthentication(self.slug) self.token = self.github_auth_obj.get_access_token() # Initialize ogr service object self._init_helper() def _init_helper(self): """Handle the initialization or reinitialization of OGR object.""" if self.service_type == ServiceType.GITHUB: if self.service_url: self.service = GithubService(self.token, instance_url=self.service_url) else: self.service = GithubService(self.token) self.repository = self.service.get_project( repo=self.repo, namespace=self.namespace) elif self.service_type == ServiceType.GITLAB: if self.service_url: self.service = GitlabService(self.token, instance_url=self.service_url) else: self.service = GitlabService(self.token) self.repository = self.service.get_project( repo=self.repo, namespace=self.namespace) else: raise NotImplementedError def refresh_access_token(decorated: Any): # noqa: N805 """Check if access token as expired and refresh if necessary.""" @functools.wraps(decorated) def wrapper(sourcemanagement, *args, **kwargs): if sourcemanagement.installation: # We check if installation is being used. if datetime.datetime.now( ) > sourcemanagement.token_expire_time: sourcemanagement.token = sourcemanagement.github_auth_obj.get_access_token( ) sourcemanagement.token_expire_time = datetime.datetime.now( ) + datetime.timedelta(minutes=9, seconds=30) return decorated(sourcemanagement, *args, **kwargs) return wrapper @refresh_access_token def get_access_token(self) -> Tuple: """Retrieve the current access token and expire time from the class variables.""" return self.token, self.token_expire_time @refresh_access_token def get_issue(self, title: str) -> Issue: """Retrieve issue with the given title.""" for issue in self.repository.get_issue_list(): if issue._raw_issue.title == title: return issue return None @refresh_access_token def open_issue_if_not_exist( self, title: str, body: typing.Callable, refresh_comment: typing.Callable = None, labels: list = None, ) -> Issue: """Open the given issue if does not exist already (as opened).""" _LOGGER.debug(f"Reporting issue {title!r}") issue = self.get_issue(title) if issue: _LOGGER.info( f"Issue already noted on upstream with title #{issue._raw_issue.title}" ) if not refresh_comment: return issue comment_body = refresh_comment(issue) if comment_body: issue.comment(comment_body) _LOGGER.info( f"Added refresh comment to issue with title #{issue._raw_issue.title}" ) else: _LOGGER.debug("Refresh comment not added") else: issue = self.repository.create_issue(title, body()) issue.add_label(*set(labels or [])) _LOGGER.info(f"Reported issue {title!r} with id #{issue.id}") return issue @refresh_access_token def close_issue_if_exists(self, title: str, comment: str = None): """Close the given issue (referenced by its title) and close it with a comment.""" issue = self.get_issue(title) if not issue: _LOGGER.debug(f"Issue {title!r} not found, not closing it") return issue.comment(comment) issue.close() @refresh_access_token def _github_assign(self, issue: Issue, assignees: typing.List[str]) -> None: """Assign the given users to a particular issue.""" data = {"assignees": assignees} response = requests.Session().post( f"{BASE_URL['github']}/repos/{self.slug}/issues/{issue.id}/assignees", headers={"Authorization": f"token {self.token}"}, json=data, ) response.raise_for_status() @refresh_access_token def _gitlab_fetch_userid(self, usernames: typing.List[str]) -> typing.List[int]: """Fetch the corresponding user ids for usernames.""" user_ids = [] for username in usernames: response = requests.Session().get( f"{BASE_URL['gitlab']}/users?username={username}", headers={"Authorization": f"token {self.token}"}, ) res = json.loads(response.text) userid = res.pop().get("id") if userid: user_ids.append(userid) return user_ids @refresh_access_token def _gitlab_assign(self, issue: Issue, assignees: typing.List[str]) -> None: """Assign the given users to a particular issue. Gitlab assignee id's are different from username.""" assignees_ids = self._gitlab_fetch_userid(assignees) data = {"assignee_ids": assignees_ids} response = requests.Session().put( f"{BASE_URL['gitlab']}/projects/{quote_plus(self.slug)}/issues/{issue.id}", params={"private_token": self.token}, json=data, ) response.raise_for_status() @refresh_access_token def assign(self, issue: Issue, assignees: typing.List[str]) -> None: """Assign users (by their accounts) to the given issue.""" # Replace with OGR methods, when implemented in OGR. if self.service_type == ServiceType.GITHUB: self._github_assign(issue, assignees) elif self.service_type == ServiceType.GITLAB: self._gitlab_assign(issue, assignees) else: raise NotImplementedError @refresh_access_token def open_merge_request(self, commit_msg: str, branch_name: str, body: str, labels: list) -> PullRequest: """Open a merge request for the given branch.""" try: if self.repository.is_fork: merge_request = self.repository.create_pr( commit_msg, body, "master", branch_name, self.namespace) else: merge_request = self.repository.create_pr( commit_msg, body, "master", branch_name) merge_request.add_label(*labels) except Exception as exc: raise CreatePRError(f"Failed to create a pull request: {exc}") else: _LOGGER.info( f"Newly created pull request #{merge_request.id} available at {merge_request.url}" ) return merge_request @refresh_access_token def _github_delete_branch(self, branch: str) -> None: """Delete the given branch from remote repository.""" response = requests.Session().delete( f"{BASE_URL['github']}/repos/{self.slug}/git/refs/heads/{branch}", headers={"Authorization": f"token {self.token}"}, ) response.raise_for_status() # GitHub returns an empty string, noting to return. @refresh_access_token def _gitlab_delete_branch(self, branch: str) -> None: """Delete the given branch from remote repository.""" response = requests.Session().delete( f"{BASE_URL['gitlab']}/projects/{quote_plus(self.slug)}/repository/branches/{branch}", params={"private_token": self.token}, ) response.raise_for_status() @refresh_access_token def list_branches(self) -> set: """Get branches available on remote.""" try: branches = self.repository.get_branches() except Exception as exc: raise CannotFetchBranchesError( f"Cannot fetch branches. Error is: {exc}") else: return branches @refresh_access_token def get_prs(self) -> list: """Get all the open PR objects as a list for a repo.""" try: prs = self.repository.get_pr_list() except Exception as exc: raise CannotFetchPRError(f"Cannot fetch PR's. Error is - {exc}") else: return prs @refresh_access_token def delete_branch(self, branch_name: str) -> None: """Delete the given branch from remote.""" # TODO: remove this logic once OGR will support branch operations if self.service_type == ServiceType.GITHUB: return self._github_delete_branch(branch_name) elif self.service_type == ServiceType.GITLAB: return self._gitlab_delete_branch(branch_name) else: raise NotImplementedError