classBitBucketRepository(ReadableDeploymentStorage):"""Interact with files stored in BitBucket repositories. An accessible installation of git is required for this block to function properly. """_block_type_name="BitBucket Repository"_logo_url="https://cdn.sanity.io/images/3ugk85nk/production/5d729f7355fb6828c4b605268ded9cfafab3ae4f-250x250.png"# noqa_description="Interact with files stored in BitBucket repositories."repository:str=Field(default=...,description="The URL of a BitBucket repository to read from in HTTPS format",)reference:Optional[str]=Field(default=None,description="An optional reference to pin to; can be a branch or tag.",)bitbucket_credentials:Optional[BitBucketCredentials]=Field(default=None,description=("An optional BitBucketCredentials block for authenticating with ""private BitBucket repos."),)@validator("bitbucket_credentials")def_ensure_credentials_go_with_https(cls,v:str,values:dict)->str:"""Ensure that credentials are not provided with 'SSH' formatted BitBucket URLs. Validators are by default only called on provided arguments. Note: validates `credentials` specifically so that it only fires when private repositories are used. """ifvisnotNone:ifurlparse(values["repository"]).scheme!="https":raiseInvalidRepositoryURLError(("Credentials can only be used with BitBucket repositories ""using the 'HTTPS' format. You must either remove the ""credential if you wish to use the 'SSH' format and are not ""using a private repository, or you must change the repository ""URL to the 'HTTPS' format."))returnvdef_create_repo_url(self)->str:"""Format the URL provided to the `git clone` command. For private repos in the cloud: https://x-token-auth:<access-token>@bitbucket.org/<user>/<repo>.git For private repos with a local bitbucket server: https://<username>:<access-token>@<server>/scm/<project>/<repo>.git All other repos should be the same as `self.repository`. """url_components=urlparse(self.repository)token_is_set=(self.bitbucket_credentialsisnotNoneandself.bitbucket_credentials.token)# Need a token for private reposifurl_components.scheme=="https"andtoken_is_set:token=self.bitbucket_credentials.token.get_secret_value()username=self.bitbucket_credentials.usernameifusernameisNone:username="x-token-auth"updated_components=url_components._replace(netloc=f"{username}:{token}@{url_components.netloc}")full_url=urlunparse(updated_components)else:full_url=self.repositoryreturnfull_url@staticmethoddef_get_paths(dst_dir:Union[str,None],src_dir:str,sub_directory:Optional[str])->Tuple[str,str]:"""Return the fully formed paths for BitBucketRepository contents. Return will take the form of (content_source, content_destination). """ifdst_dirisNone:content_destination=Path(".").absolute()else:content_destination=Path(dst_dir)content_source=Path(src_dir)ifsub_directory:content_destination=content_destination.joinpath(sub_directory)content_source=content_source.joinpath(sub_directory)returnstr(content_source),str(content_destination)@sync_compatibleasyncdefget_directory(self,from_path:Optional[str]=None,local_path:Optional[str]=None)->None:"""Clones a BitBucket project within `from_path` to the provided `local_path`. This defaults to cloning the repository reference configured on the Block to the present working directory. Args: from_path: If provided, interpreted as a subdirectory of the underlying repository that will be copied to the provided local path. local_path: A local path to clone to; defaults to present working directory. """# Construct commandcmd=["git","clone",self._create_repo_url()]ifself.reference:cmd+=["-b",self.reference]# Limit git historycmd+=["--depth","1"]# Clone to a temporary directory and move the subdirectory overwithTemporaryDirectory(suffix="prefect")astmp_dir:cmd.append(tmp_dir)err_stream=io.StringIO()out_stream=io.StringIO()process=awaitrun_process(cmd,stream_output=(out_stream,err_stream))ifprocess.returncode!=0:err_stream.seek(0)raiseOSError(f"Failed to pull from remote:\n{err_stream.read()}")content_source,content_destination=self._get_paths(dst_dir=local_path,src_dir=tmp_dir,sub_directory=from_path)copy_tree(src=content_source,dst=content_destination)
@sync_compatibleasyncdefget_directory(self,from_path:Optional[str]=None,local_path:Optional[str]=None)->None:"""Clones a BitBucket project within `from_path` to the provided `local_path`. This defaults to cloning the repository reference configured on the Block to the present working directory. Args: from_path: If provided, interpreted as a subdirectory of the underlying repository that will be copied to the provided local path. local_path: A local path to clone to; defaults to present working directory. """# Construct commandcmd=["git","clone",self._create_repo_url()]ifself.reference:cmd+=["-b",self.reference]# Limit git historycmd+=["--depth","1"]# Clone to a temporary directory and move the subdirectory overwithTemporaryDirectory(suffix="prefect")astmp_dir:cmd.append(tmp_dir)err_stream=io.StringIO()out_stream=io.StringIO()process=awaitrun_process(cmd,stream_output=(out_stream,err_stream))ifprocess.returncode!=0:err_stream.seek(0)raiseOSError(f"Failed to pull from remote:\n{err_stream.read()}")content_source,content_destination=self._get_paths(dst_dir=local_path,src_dir=tmp_dir,sub_directory=from_path)copy_tree(src=content_source,dst=content_destination)