Repository: PrefectHQ/prefect

Issue:
Remove deprecated storage blocks from `main`
Remove the following storage blocks:
- `prefect.filesystems.S3`
- `prefect.filesystems.GCS`
- `prefect.filesystems.Azure`
- `prefect.filesystems.GitHub`

These classes will be included with 2.x releases, but will not be included in the 3.0 release.


Discussion:

Resolving patch:
diff --git a/src/prefect/filesystems.py b/src/prefect/filesystems.py
--- a/src/prefect/filesystems.py
+++ b/src/prefect/filesystems.py
@@ -1,27 +1,20 @@
 import abc
-import io
-import json
 import urllib.parse
 from pathlib import Path
-from shutil import ignore_patterns
-from tempfile import TemporaryDirectory
-from typing import Any, Dict, Optional, Tuple, Union
+from typing import Any, Dict, Optional
 
 import anyio
 import fsspec
 from pydantic.v1 import Field, SecretStr, validator
 
-from prefect._internal.compatibility.deprecated import deprecated_class
 from prefect._internal.schemas.validators import (
     stringify_path,
     validate_basepath,
-    validate_github_access_token,
 )
 from prefect.blocks.core import Block
 from prefect.utilities.asyncutils import run_sync_in_worker_thread, sync_compatible
 from prefect.utilities.compat import copytree
 from prefect.utilities.filesystem import filter_files
-from prefect.utilities.processutils import run_process
 
 
 class ReadableFileSystem(Block, abc.ABC):
@@ -416,368 +409,6 @@ def filesystem(self) -> fsspec.AbstractFileSystem:
         return self._filesystem
 
 
-@deprecated_class(
-    start_date="Mar 2024", help="Use the `S3Bucket` block from prefect-aws instead."
-)
-class S3(WritableFileSystem, WritableDeploymentStorage):
-    """
-    DEPRECATION WARNING:
-
-    This class is deprecated as of March 2024 and will not be available after September 2024.
-    It has been replaced by `S3Bucket` from the `prefect-aws` package, which offers enhanced functionality
-    and better a better user experience.
-
-    Store data as a file on AWS S3.
-
-    Example:
-        Load stored S3 config:
-        ```python
-        from prefect.filesystems import S3
-
-        s3_block = S3.load("BLOCK_NAME")
-        ```
-    """
-
-    _block_type_name = "S3"
-    _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/d74b16fe84ce626345adf235a47008fea2869a60-225x225.png"
-    _documentation_url = "https://docs.prefect.io/concepts/filesystems/#s3"
-
-    bucket_path: str = Field(
-        default=...,
-        description="An S3 bucket path.",
-        examples=["my-bucket/a-directory-within"],
-    )
-    aws_access_key_id: Optional[SecretStr] = Field(
-        default=None,
-        title="AWS Access Key ID",
-        description="Equivalent to the AWS_ACCESS_KEY_ID environment variable.",
-        examples=["AKIAIOSFODNN7EXAMPLE"],
-    )
-    aws_secret_access_key: Optional[SecretStr] = Field(
-        default=None,
-        title="AWS Secret Access Key",
-        description="Equivalent to the AWS_SECRET_ACCESS_KEY environment variable.",
-        examples=["wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"],
-    )
-
-    _remote_file_system: RemoteFileSystem = None
-
-    @property
-    def basepath(self) -> str:
-        return f"s3://{self.bucket_path}"
-
-    @property
-    def filesystem(self) -> RemoteFileSystem:
-        settings = {}
-        if self.aws_access_key_id:
-            settings["key"] = self.aws_access_key_id.get_secret_value()
-        if self.aws_secret_access_key:
-            settings["secret"] = self.aws_secret_access_key.get_secret_value()
-        self._remote_file_system = RemoteFileSystem(
-            basepath=f"s3://{self.bucket_path}", settings=settings
-        )
-        return self._remote_file_system
-
-    @sync_compatible
-    async def get_directory(
-        self, from_path: Optional[str] = None, local_path: Optional[str] = None
-    ) -> bytes:
-        """
-        Downloads a directory from a given remote path to a local directory.
-
-        Defaults to downloading the entire contents of the block's basepath to the current working directory.
-        """
-        return await self.filesystem.get_directory(
-            from_path=from_path, local_path=local_path
-        )
-
-    @sync_compatible
-    async def put_directory(
-        self,
-        local_path: Optional[str] = None,
-        to_path: Optional[str] = None,
-        ignore_file: Optional[str] = None,
-    ) -> int:
-        """
-        Uploads a directory from a given local path to a remote directory.
-
-        Defaults to uploading the entire contents of the current working directory to the block's basepath.
-        """
-        return await self.filesystem.put_directory(
-            local_path=local_path, to_path=to_path, ignore_file=ignore_file
-        )
-
-    @sync_compatible
-    async def read_path(self, path: str) -> bytes:
-        return await self.filesystem.read_path(path)
-
-    @sync_compatible
-    async def write_path(self, path: str, content: bytes) -> str:
-        return await self.filesystem.write_path(path=path, content=content)
-
-
-@deprecated_class(
-    start_date="Mar 2024", help="Use the `GcsBucket` block from prefect-gcp instead."
-)
-class GCS(WritableFileSystem, WritableDeploymentStorage):
-    """
-    DEPRECATION WARNING:
-
-    This class is deprecated as of March 2024 and will not be available after September 2024.
-    It has been replaced by `GcsBucket` from the `prefect-gcp` package, which offers enhanced functionality
-    and better a better user experience.
-    Store data as a file on Google Cloud Storage.
-
-    Example:
-        Load stored GCS config:
-        ```python
-        from prefect.filesystems import GCS
-
-        gcs_block = GCS.load("BLOCK_NAME")
-        ```
-    """
-
-    _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/422d13bb838cf247eb2b2cf229ce6a2e717d601b-256x256.png"
-    _documentation_url = "https://docs.prefect.io/concepts/filesystems/#gcs"
-
-    bucket_path: str = Field(
-        default=...,
-        description="A GCS bucket path.",
-        examples=["my-bucket/a-directory-within"],
-    )
-    service_account_info: Optional[SecretStr] = Field(
-        default=None,
-        description="The contents of a service account keyfile as a JSON string.",
-    )
-    project: Optional[str] = Field(
-        default=None,
-        description=(
-            "The project the GCS bucket resides in. If not provided, the project will"
-            " be inferred from the credentials or environment."
-        ),
-    )
-
-    @property
-    def basepath(self) -> str:
-        return f"gcs://{self.bucket_path}"
-
-    @property
-    def filesystem(self) -> RemoteFileSystem:
-        settings = {}
-        if self.service_account_info:
-            try:
-                settings["token"] = json.loads(
-                    self.service_account_info.get_secret_value()
-                )
-            except json.JSONDecodeError:
-                raise ValueError(
-                    "Unable to load provided service_account_info. Please make sure"
-                    " that the provided value is a valid JSON string."
-                )
-        remote_file_system = RemoteFileSystem(
-            basepath=f"gcs://{self.bucket_path}", settings=settings
-        )
-        return remote_file_system
-
-    @sync_compatible
-    async def get_directory(
-        self, from_path: Optional[str] = None, local_path: Optional[str] = None
-    ) -> bytes:
-        """
-        Downloads a directory from a given remote path to a local directory.
-
-        Defaults to downloading the entire contents of the block's basepath to the current working directory.
-        """
-        return await self.filesystem.get_directory(
-            from_path=from_path, local_path=local_path
-        )
-
-    @sync_compatible
-    async def put_directory(
-        self,
-        local_path: Optional[str] = None,
-        to_path: Optional[str] = None,
-        ignore_file: Optional[str] = None,
-    ) -> int:
-        """
-        Uploads a directory from a given local path to a remote directory.
-
-        Defaults to uploading the entire contents of the current working directory to the block's basepath.
-        """
-        return await self.filesystem.put_directory(
-            local_path=local_path, to_path=to_path, ignore_file=ignore_file
-        )
-
-    @sync_compatible
-    async def read_path(self, path: str) -> bytes:
-        return await self.filesystem.read_path(path)
-
-    @sync_compatible
-    async def write_path(self, path: str, content: bytes) -> str:
-        return await self.filesystem.write_path(path=path, content=content)
-
-
-@deprecated_class(
-    start_date="Mar 2024",
-    help="Use the `AzureBlobStorageContainer` block from prefect-azure instead.",
-)
-class Azure(WritableFileSystem, WritableDeploymentStorage):
-    """
-    DEPRECATION WARNING:
-
-    This class is deprecated as of March 2024 and will not be available after September 2024.
-    It has been replaced by `AzureBlobStorageContainer` from the `prefect-azure` package, which
-    offers enhanced functionality and better a better user experience.
-
-    Store data as a file on Azure Datalake and Azure Blob Storage.
-
-    Example:
-        Load stored Azure config:
-        ```python
-        from prefect.filesystems import Azure
-
-        az_block = Azure.load("BLOCK_NAME")
-        ```
-    """
-
-    _block_type_name = "Azure"
-    _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/54e3fa7e00197a4fbd1d82ed62494cb58d08c96a-250x250.png"
-    _documentation_url = "https://docs.prefect.io/concepts/filesystems/#azure"
-
-    bucket_path: str = Field(
-        default=...,
-        description="An Azure storage bucket path.",
-        examples=["my-bucket/a-directory-within"],
-    )
-    azure_storage_connection_string: Optional[SecretStr] = Field(
-        default=None,
-        title="Azure storage connection string",
-        description=(
-            "Equivalent to the AZURE_STORAGE_CONNECTION_STRING environment variable."
-        ),
-    )
-    azure_storage_account_name: Optional[SecretStr] = Field(
-        default=None,
-        title="Azure storage account name",
-        description=(
-            "Equivalent to the AZURE_STORAGE_ACCOUNT_NAME environment variable."
-        ),
-    )
-    azure_storage_account_key: Optional[SecretStr] = Field(
-        default=None,
-        title="Azure storage account key",
-        description="Equivalent to the AZURE_STORAGE_ACCOUNT_KEY environment variable.",
-    )
-    azure_storage_tenant_id: Optional[SecretStr] = Field(
-        None,
-        title="Azure storage tenant ID",
-        description="Equivalent to the AZURE_TENANT_ID environment variable.",
-    )
-    azure_storage_client_id: Optional[SecretStr] = Field(
-        None,
-        title="Azure storage client ID",
-        description="Equivalent to the AZURE_CLIENT_ID environment variable.",
-    )
-    azure_storage_client_secret: Optional[SecretStr] = Field(
-        None,
-        title="Azure storage client secret",
-        description="Equivalent to the AZURE_CLIENT_SECRET environment variable.",
-    )
-    azure_storage_anon: bool = Field(
-        default=True,
-        title="Azure storage anonymous connection",
-        description=(
-            "Set the 'anon' flag for ADLFS. This should be False for systems that"
-            " require ADLFS to use DefaultAzureCredentials."
-        ),
-    )
-    azure_storage_container: Optional[SecretStr] = Field(
-        default=None,
-        title="Azure storage container",
-        description=(
-            "Blob Container in Azure Storage Account. If set the 'bucket_path' will"
-            " be interpreted using the following URL format:"
-            "'az://<container>@<storage_account>.dfs.core.windows.net/<bucket_path>'."
-        ),
-    )
-    _remote_file_system: RemoteFileSystem = None
-
-    @property
-    def basepath(self) -> str:
-        if self.azure_storage_container:
-            return (
-                f"az://{self.azure_storage_container.get_secret_value()}"
-                f"@{self.azure_storage_account_name.get_secret_value()}"
-                f".dfs.core.windows.net/{self.bucket_path}"
-            )
-        else:
-            return f"az://{self.bucket_path}"
-
-    @property
-    def filesystem(self) -> RemoteFileSystem:
-        settings = {}
-        if self.azure_storage_connection_string:
-            settings[
-                "connection_string"
-            ] = self.azure_storage_connection_string.get_secret_value()
-        if self.azure_storage_account_name:
-            settings[
-                "account_name"
-            ] = self.azure_storage_account_name.get_secret_value()
-        if self.azure_storage_account_key:
-            settings["account_key"] = self.azure_storage_account_key.get_secret_value()
-        if self.azure_storage_tenant_id:
-            settings["tenant_id"] = self.azure_storage_tenant_id.get_secret_value()
-        if self.azure_storage_client_id:
-            settings["client_id"] = self.azure_storage_client_id.get_secret_value()
-        if self.azure_storage_client_secret:
-            settings[
-                "client_secret"
-            ] = self.azure_storage_client_secret.get_secret_value()
-        settings["anon"] = self.azure_storage_anon
-        self._remote_file_system = RemoteFileSystem(
-            basepath=self.basepath, settings=settings
-        )
-        return self._remote_file_system
-
-    @sync_compatible
-    async def get_directory(
-        self, from_path: Optional[str] = None, local_path: Optional[str] = None
-    ) -> bytes:
-        """
-        Downloads a directory from a given remote path to a local directory.
-
-        Defaults to downloading the entire contents of the block's basepath to the current working directory.
-        """
-        return await self.filesystem.get_directory(
-            from_path=from_path, local_path=local_path
-        )
-
-    @sync_compatible
-    async def put_directory(
-        self,
-        local_path: Optional[str] = None,
-        to_path: Optional[str] = None,
-        ignore_file: Optional[str] = None,
-    ) -> int:
-        """
-        Uploads a directory from a given local path to a remote directory.
-
-        Defaults to uploading the entire contents of the current working directory to the block's basepath.
-        """
-        return await self.filesystem.put_directory(
-            local_path=local_path, to_path=to_path, ignore_file=ignore_file
-        )
-
-    @sync_compatible
-    async def read_path(self, path: str) -> bytes:
-        return await self.filesystem.read_path(path)
-
-    @sync_compatible
-    async def write_path(self, path: str, content: bytes) -> str:
-        return await self.filesystem.write_path(path=path, content=content)
-
-
 class SMB(WritableFileSystem, WritableDeploymentStorage):
     """
     Store data as a file on a SMB share.
@@ -875,139 +506,3 @@ async def read_path(self, path: str) -> bytes:
     @sync_compatible
     async def write_path(self, path: str, content: bytes) -> str:
         return await self.filesystem.write_path(path=path, content=content)
-
-
-@deprecated_class(
-    start_date="Mar 2024",
-    help="Use the `GitHubRepository` block from prefect-github instead.",
-)
-class GitHub(ReadableDeploymentStorage):
-    """
-        DEPRECATION WARNING:
-
-        This class is deprecated as of March 2024 and will not be available after September 2024.
-        It has been replaced by `GitHubRepository` from the `prefect-github` package, which offers
-        enhanced functionality and better a better user experience.
-    q
-        Interact with files stored on GitHub repositories.
-    """
-
-    _block_type_name = "GitHub"
-    _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/41971cfecfea5f79ff334164f06ecb34d1038dd4-250x250.png"
-    _documentation_url = "https://docs.prefect.io/concepts/filesystems/#github"
-
-    repository: str = Field(
-        default=...,
-        description=(
-            "The URL of a GitHub repository to read from, in either HTTPS or SSH"
-            " format."
-        ),
-    )
-    reference: Optional[str] = Field(
-        default=None,
-        description="An optional reference to pin to; can be a branch name or tag.",
-    )
-    access_token: Optional[SecretStr] = Field(
-        name="Personal Access Token",
-        default=None,
-        description=(
-            "A GitHub Personal Access Token (PAT) with repo scope."
-            " To use a fine-grained PAT, provide '{username}:{PAT}' as the value."
-        ),
-    )
-    include_git_objects: bool = Field(
-        default=True,
-        description=(
-            "Whether to include git objects when copying the repo contents to a"
-            " directory."
-        ),
-    )
-
-    @validator("access_token")
-    def _ensure_credentials_go_with_https(cls, v: str, values: dict) -> str:
-        return validate_github_access_token(v, values)
-
-    def _create_repo_url(self) -> str:
-        """Format the URL provided to the `git clone` command.
-
-        For private repos: https://<oauth-key>@github.com/<username>/<repo>.git
-        All other repos should be the same as `self.repository`.
-        """
-        url_components = urllib.parse.urlparse(self.repository)
-        if url_components.scheme == "https" and self.access_token is not None:
-            updated_components = url_components._replace(
-                netloc=f"{self.access_token.get_secret_value()}@{url_components.netloc}"
-            )
-            full_url = urllib.parse.urlunparse(updated_components)
-        else:
-            full_url = self.repository
-
-        return full_url
-
-    @staticmethod
-    def _get_paths(
-        dst_dir: Union[str, None], src_dir: str, sub_directory: str
-    ) -> Tuple[str, str]:
-        """Returns the fully formed paths for GitHubRepository contents in the form
-        (content_source, content_destination).
-        """
-        if dst_dir is None:
-            content_destination = Path(".").absolute()
-        else:
-            content_destination = Path(dst_dir)
-
-        content_source = Path(src_dir)
-
-        if sub_directory:
-            content_destination = content_destination.joinpath(sub_directory)
-            content_source = content_source.joinpath(sub_directory)
-
-        return str(content_source), str(content_destination)
-
-    @sync_compatible
-    async def get_directory(
-        self, from_path: Optional[str] = None, local_path: Optional[str] = None
-    ) -> None:
-        """
-        Clones a GitHub project specified in `from_path` to the provided `local_path`;
-        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 COMMAND
-        cmd = ["git", "clone", self._create_repo_url()]
-        if self.reference:
-            cmd += ["-b", self.reference]
-
-        # Limit git history
-        cmd += ["--depth", "1"]
-
-        # Clone to a temporary directory and move the subdirectory over
-        with TemporaryDirectory(suffix="prefect") as tmp_dir:
-            cmd.append(tmp_dir)
-
-            err_stream = io.StringIO()
-            out_stream = io.StringIO()
-            process = await run_process(cmd, stream_output=(out_stream, err_stream))
-            if process.returncode != 0:
-                err_stream.seek(0)
-                raise OSError(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
-            )
-
-            ignore_func = None
-            if not self.include_git_objects:
-                ignore_func = ignore_patterns(".git")
-
-            copytree(
-                src=content_source,
-                dst=content_destination,
-                dirs_exist_ok=True,
-                ignore=ignore_func,
-            )


Test patch:
diff --git a/tests/cli/test_block.py b/tests/cli/test_block.py
--- a/tests/cli/test_block.py
+++ b/tests/cli/test_block.py
@@ -206,14 +206,12 @@ def test_listing_system_block_types(register_block_types):
         "slack",
         "date-time",
         "docker-container",
-        "gcs",
         "json",
         "kubernetes-cluster-config",
         "kubernetes-job",
         "local-file-system",
         "process",
         "remote-file-system",
-        "s3",
         "secret",
         "slack-webhook",
     )
diff --git a/tests/test_filesystems.py b/tests/test_filesystems.py
--- a/tests/test_filesystems.py
+++ b/tests/test_filesystems.py
@@ -6,17 +6,11 @@
 import pytest
 
 import prefect
-from prefect._internal.compatibility.deprecated import PrefectDeprecationWarning
-from prefect.exceptions import InvalidRepositoryURLError
 from prefect.filesystems import (
-    GCS,
-    S3,
-    Azure,
-    GitHub,
     LocalFileSystem,
     RemoteFileSystem,
 )
-from prefect.testing.utilities import AsyncMock, MagicMock
+from prefect.testing.utilities import MagicMock
 from prefect.utilities.filesystem import tmpchdir
 
 TEST_PROJECTS_DIR = prefect.__development_base_path__ / "tests" / "test-projects"
@@ -51,44 +45,6 @@ def setup_test_directory(tmp_src: str, sub_dir: str = "puppy") -> Tuple[str, str
     return parent_contents, child_contents
 
 
-@pytest.mark.parametrize(
-    "Filesystem,kwargs,expected_message",
-    [
-        (
-            S3,
-            {"bucket_path": "bucket/path"},
-            "prefect.filesystems.S3 has been deprecated."
-            " It will not be available after Sep 2024."
-            " Use the `S3Bucket` block from prefect-aws instead.",
-        ),
-        (
-            GCS,
-            {"bucket_path": "bucket/path"},
-            "prefect.filesystems.GCS has been deprecated."
-            " It will not be available after Sep 2024."
-            " Use the `GcsBucket` block from prefect-gcp instead.",
-        ),
-        (
-            Azure,
-            {"bucket_path": "bucket/path"},
-            "prefect.filesystems.Azure has been deprecated."
-            " It will not be available after Sep 2024."
-            " Use the `AzureBlobStorageContainer` block from prefect-azure instead.",
-        ),
-        (
-            GitHub,
-            {"repository": "https://github.com/org/repo.git"},
-            "prefect.filesystems.GitHub has been deprecated."
-            " It will not be available after Sep 2024."
-            " Use the `GitHubRepository` block from prefect-github instead.",
-        ),
-    ],
-)
-def test_deprecated_filesystems_raise_a_warning(Filesystem, kwargs, expected_message):
-    with pytest.warns(PrefectDeprecationWarning, match=expected_message):
-        Filesystem(**kwargs)
-
-
 class TestLocalFileSystem:
     async def test_read_write_roundtrip(self, tmp_path):
         fs = LocalFileSystem(basepath=str(tmp_path))
@@ -490,293 +446,3 @@ async def test_put_directory_empty_from_path_uses_basepath(
             fs = LocalFileSystem(basepath=base_path)
             await fs.put_directory(to_path=null_value, local_path=local_path)
         assert (local_path / "test").exists()
-
-
-class TestGitHub:
-    class MockTmpDir:
-        """Utility for having `TemporaryDirectory` return a known location."""
-
-        dir = None
-
-        def __init__(self, *args, **kwargs):
-            pass
-
-        def __enter__(self):
-            return self.dir
-
-        def __exit__(self, *args, **kwargs):
-            pass
-
-    async def test_subprocess_errors_are_surfaced(self):
-        g = GitHub(repository="incorrect-url-scheme")
-        with pytest.raises(
-            OSError, match="fatal: repository 'incorrect-url-scheme' does not exist"
-        ):
-            await g.get_directory()
-
-    async def test_repository_default(self, monkeypatch):
-        class p:
-            returncode = 0
-
-        mock = AsyncMock(return_value=p())
-        monkeypatch.setattr(prefect.filesystems, "run_process", mock)
-        g = GitHub(repository="prefect")
-        await g.get_directory()
-
-        assert mock.await_count == 1
-        expected_cmd = ["git", "clone", "prefect"]
-        assert mock.await_args[0][0][: len(expected_cmd)] == expected_cmd
-
-    async def test_reference_default(self, monkeypatch):
-        class p:
-            returncode = 0
-
-        mock = AsyncMock(return_value=p())
-        monkeypatch.setattr(prefect.filesystems, "run_process", mock)
-        g = GitHub(repository="prefect", reference="2.0.0")
-        await g.get_directory()
-
-        assert mock.await_count == 1
-        expected_cmd = ["git", "clone", "prefect", "-b", "2.0.0", "--depth", "1"]
-        assert mock.await_args[0][0][: len(expected_cmd)] == expected_cmd
-
-    async def test_token_added_correctly_from_credential(self, monkeypatch):
-        """Ensure that the repo url is in the format `https://<oauth-key>@github.com/<username>/<repo>.git`."""  # noqa: E501
-
-        class p:
-            returncode = 0
-
-        mock = AsyncMock(return_value=p())
-        monkeypatch.setattr(prefect.filesystems, "run_process", mock)
-        credential = "XYZ"
-        repo = "https://github.com/PrefectHQ/prefect.git"
-        g = GitHub(
-            repository=repo,
-            access_token=credential,
-        )
-        await g.get_directory()
-        assert mock.await_count == 1
-        expected_cmd = [
-            "git",
-            "clone",
-            f"https://{credential}@github.com/PrefectHQ/prefect.git",
-            "--depth",
-            "1",
-        ]
-        assert mock.await_args[0][0][: len(expected_cmd)] == expected_cmd
-
-    async def test_ssh_fails_with_credential(self, monkeypatch):
-        """Ensure that credentials cannot be passed in if the URL is not in the HTTPS
-        format.
-        """
-
-        class p:
-            returncode = 0
-
-        mock = AsyncMock(return_value=p())
-        monkeypatch.setattr(prefect.filesystems, "run_process", mock)
-        credential = "XYZ"
-        error_msg = "Crendentials can only be used with GitHub repositories using the 'HTTPS' format"  # noqa
-        with pytest.raises(InvalidRepositoryURLError, match=error_msg):
-            GitHub(
-                repository="git@github.com:PrefectHQ/prefect.git",
-                access_token=credential,
-            )
-
-    async def test_dir_contents_copied_correctly_with_get_directory(
-        self, monkeypatch, tmp_path
-    ):  # noqa
-        """Check that `get_directory` is able to correctly copy contents from src->dst"""  # noqa
-
-        class p:
-            returncode = 0
-
-        mock = AsyncMock(return_value=p())
-        monkeypatch.setattr(prefect.filesystems, "run_process", mock)
-
-        sub_dir_name = "puppy"
-
-        parent_contents, child_contents = setup_test_directory(tmp_path, sub_dir_name)
-        self.MockTmpDir.dir = tmp_path
-
-        # move file contents to tmp_dst
-        with TemporaryDirectory() as tmp_dst:
-            monkeypatch.setattr(
-                prefect.filesystems,
-                "TemporaryDirectory",
-                self.MockTmpDir,
-            )
-
-            g = GitHub(
-                repository="https://github.com/PrefectHQ/prefect.git",
-            )
-            await g.get_directory(local_path=tmp_dst)
-
-            assert set(os.listdir(tmp_dst)) == parent_contents
-            assert set(os.listdir(Path(tmp_dst) / sub_dir_name)) == child_contents
-
-    async def test_dir_contents_copied_correctly_with_get_directory_and_from_path(
-        self, monkeypatch, tmp_path
-    ):  # noqa
-        """Check that `get_directory` is able to correctly copy contents from src->dst
-        when `from_path` is included.
-
-        It is expected that the directory specified by `from_path` will be moved to the
-        specified destination, along with all of its contents.
-        """
-
-        class p:
-            returncode = 0
-
-        mock = AsyncMock(return_value=p())
-        monkeypatch.setattr(prefect.filesystems, "run_process", mock)
-
-        sub_dir_name = "puppy"
-
-        parent_contents, child_contents = setup_test_directory(tmp_path, sub_dir_name)
-        self.MockTmpDir.dir = tmp_path
-
-        # move file contents to tmp_dst
-        with TemporaryDirectory() as tmp_dst:
-            monkeypatch.setattr(
-                prefect.filesystems,
-                "TemporaryDirectory",
-                self.MockTmpDir,
-            )
-
-            g = GitHub(
-                repository="https://github.com/PrefectHQ/prefect.git",
-            )
-            await g.get_directory(local_path=tmp_dst, from_path=sub_dir_name)
-
-            assert set(os.listdir(tmp_dst)) == set([sub_dir_name])
-            assert set(os.listdir(Path(tmp_dst) / sub_dir_name)) == child_contents
-
-    @pytest.mark.parametrize(
-        "include_git_objects, expect_git_objects",
-        [(True, True), (False, False), (None, True)],
-    )
-    async def test_dir_contents_copied_correctly_with_include_git_object(
-        self,
-        monkeypatch,
-        tmp_path,
-        include_git_objects,
-        expect_git_objects,
-    ):
-        """Check that `get_directory` is able to correctly copy contents from src->dst
-        with the `include_git_object`.
-
-        Current default behavior is to include git objects.
-        """
-
-        class p:
-            returncode = 0
-
-        mock = AsyncMock(return_value=p())
-        monkeypatch.setattr(prefect.filesystems, "run_process", mock)
-
-        sub_dir_name = "puppy"
-
-        parent_contents, child_contents = setup_test_directory(tmp_path, sub_dir_name)
-        self.MockTmpDir.dir = tmp_path
-
-        # add a git object to the directory
-        dot_git_file = Path(tmp_path) / ".git/test"
-        dot_git_file.parent.mkdir(parents=True, exist_ok=True)
-        dot_git_file.touch()
-
-        # move file contents to tmp_dst
-        with TemporaryDirectory() as tmp_dst:
-            monkeypatch.setattr(
-                prefect.filesystems,
-                "TemporaryDirectory",
-                self.MockTmpDir,
-            )
-
-            if include_git_objects is None:
-                # Check default behavior is to include git objects
-                g = GitHub(
-                    repository="https://github.com/PrefectHQ/prefect.git",
-                )
-            else:
-                g = GitHub(
-                    repository="https://github.com/PrefectHQ/prefect.git",
-                    include_git_objects=include_git_objects,
-                )
-            await g.get_directory(local_path=tmp_dst)
-
-            assert any(".git" in f for f in os.listdir(tmp_dst)) == expect_git_objects
-
-
-class TestAzure:
-    def test_init(self, monkeypatch):
-        remote_storage_mock = MagicMock()
-        monkeypatch.setattr("prefect.filesystems.RemoteFileSystem", remote_storage_mock)
-        Azure(
-            azure_storage_tenant_id="tenant",
-            azure_storage_account_name="account",
-            azure_storage_client_id="client_id",
-            azure_storage_account_key="key",
-            azure_storage_client_secret="secret",
-            bucket_path="bucket",
-        ).filesystem
-        remote_storage_mock.assert_called_once_with(
-            basepath="az://bucket",
-            settings={
-                "account_name": "account",
-                "account_key": "key",
-                "tenant_id": "tenant",
-                "client_id": "client_id",
-                "client_secret": "secret",
-                "anon": True,
-            },
-        )
-
-    def test_init_with_anon(self, monkeypatch):
-        remote_storage_mock = MagicMock()
-        monkeypatch.setattr("prefect.filesystems.RemoteFileSystem", remote_storage_mock)
-        Azure(
-            azure_storage_tenant_id="tenant",
-            azure_storage_account_name="account",
-            azure_storage_client_id="client_id",
-            azure_storage_account_key="key",
-            azure_storage_client_secret="secret",
-            bucket_path="bucket",
-            azure_storage_anon=False,
-        ).filesystem
-        remote_storage_mock.assert_called_once_with(
-            basepath="az://bucket",
-            settings={
-                "account_name": "account",
-                "account_key": "key",
-                "tenant_id": "tenant",
-                "client_id": "client_id",
-                "client_secret": "secret",
-                "anon": False,
-            },
-        )
-
-    def test_init_with_container(self, monkeypatch):
-        remote_storage_mock = MagicMock()
-        monkeypatch.setattr("prefect.filesystems.RemoteFileSystem", remote_storage_mock)
-        Azure(
-            azure_storage_tenant_id="tenant",
-            azure_storage_account_name="account",
-            azure_storage_client_id="client_id",
-            azure_storage_account_key="key",
-            azure_storage_client_secret="secret",
-            azure_storage_container="container",
-            bucket_path="bucket",
-            azure_storage_anon=False,
-        ).filesystem
-        remote_storage_mock.assert_called_once_with(
-            basepath="az://container@account.dfs.core.windows.net/bucket",
-            settings={
-                "account_name": "account",
-                "account_key": "key",
-                "tenant_id": "tenant",
-                "client_id": "client_id",
-                "client_secret": "secret",
-                "anon": False,
-            },
-        )