text: import os
  import shutil
  from typing import Dict
  
  from pybuilder.core import Project, Logger
  from pybuilder.errors import BuildFailedException
  from pybuilder.reactor import Reactor
  
  from pybuilder_integration import exec_utility
  from pybuilder_integration.directory_utility import get_latest_distribution_directory, \
      get_latest_zipped_distribution_directory
  from pybuilder_integration.properties import INTEGRATION_ARTIFACT_BUCKET, ENVIRONMENT, ROLE, APPLICATION, \
      APPLICATION_GROUP, ARTIFACT_MANAGER
  
  
  class ArtifactManager:
      def __init__(self, name, identifier):
          self.identifier = identifier
          self.friendly_name = name
  
      def upload(self, file: str, project: Project, logger: Logger, reactor: Reactor):
          pass
  
      def download_artifacts(self, project: Project, logger: Logger, reactor: Reactor):
          pass
  
  
  class S3ArtifactManager(ArtifactManager):
  
      def __init__(self):
          super().__init__("AWS S3 Artifact Manager", "S3")
  
      def upload(self, file: str, project: Project, logger: Logger, reactor: Reactor):
          if project.get_property("abort_upload","false") != "false":
              return
          # First make sure bucket exists
          self.create_bucket(logger, project, reactor)
          relative_path = get_latest_artifact_destination(logger, project)
          self._s3_transfer(file, relative_path, project, reactor, logger, recursive=False)
          relative_path = get_versioned_artifact_destination(logger, project)
          self._s3_transfer(file, relative_path, project, reactor, logger, recursive=False)
  
      def download_artifacts(self, project: Project, logger: Logger, reactor: Reactor):
          # this is a noop if there is no bucket
          if not self.does_bucket_exist(logger, project, reactor):
              return
          s3_location = get_latest_artifact_destination(logger, project)
          zipped_directory = get_latest_zipped_distribution_directory(project)
          self._s3_transfer(source=s3_location,
                            destination=zipped_directory,
                            project=project,
                            logger=logger,
                            reactor=reactor)
          return _unzip_downloaded_artifacts(zipped_directory, get_latest_distribution_directory(project), logger)
  
      @staticmethod
      def _s3_transfer(source, destination, project, reactor, logger, recursive=True):
          logger.info(f"Proceeding to transfer {source} to {destination}")
          S3ArtifactManager.verify_aws_cli(reactor)
          #  aws s3 cp myDir s3://mybucket/ --recursive
          args = [
              's3',
              'cp',
              source,
              destination
          ]
          if recursive:
              args.append("--recursive")
          exec_utility.exec_command(command_name='aws',
                                    args=args,
                                    failure_message=f"Failed to transfer integration artifacts to {destination}",
                                    log_file_name='s3-artifact-transfer',
                                    project=project,
                                    reactor=reactor,
                                    logger=logger,
                                    report=False)
  
      @staticmethod
      def verify_aws_cli(reactor):
          reactor.pybuilder_venv.verify_can_execute(command_and_arguments=["aws", "--version"],
                                                    prerequisite="aws cli",
                                                    caller="integration_tests")
  
      def create_bucket(self, logger, project, reactor):
          app_group, app_name, bucket, environment, role = get_project_metadata(logger, project)
          S3ArtifactManager.verify_aws_cli(reactor)
          res = self.does_bucket_exist(logger, project, reactor)
          if res:
              return
          exec_utility.exec_command(command_name='aws',
                                    args=[
                                        's3api',
                                        'create-bucket',
                                        '--acl',
                                        'private',
                                        '--bucket',
                                        bucket
                                    ],
                                    failure_message=f"Failed to create bucket",
                                    log_file_name='s3-create-bucket',
                                    project=project,
                                    reactor=reactor,
                                    logger=logger,
                                    report=False)
  
      def does_bucket_exist(self, logger, project, reactor):
          app_group, app_name, bucket, environment, role = get_project_metadata(logger, project)
          return exec_utility.exec_command(command_name='aws',
                                           args=[
                                               's3api',
                                               'head-bucket',
                                               '--bucket',
                                               bucket
                                           ],
                                           failure_message=f"Failed to find bucket",
                                           log_file_name='s3-head-bucket',
                                           project=project,
                                           reactor=reactor,
                                           logger=logger,
                                           raise_exception=False,
                                           report=False)
  
  
  artifact_managers: Dict[str, S3ArtifactManager] = {}
  manager = S3ArtifactManager()
  artifact_managers[manager.identifier] = manager
  
  
  def get_artifact_manager(project: Project) -> ArtifactManager:
      manager_id = project.get_property(ARTIFACT_MANAGER, "S3")
      global artifact_managers
      manager = artifact_managers.get(manager_id)
      if not manager:
          raise BuildFailedException(f"Failed to find appropriate artifact manager for {manager_id}")
      return manager
  
  
  def extract_application_role(logger, project):
      # expect a fully populated naming scheme
      app_group = project.get_property(APPLICATION_GROUP)
      app_name = project.get_property(APPLICATION)
      role = project.get_property(ROLE)
      #  if we do not have role set, assume it is all baked into the project name
      if not role:
          project_name = project.name
          if project_name.find("-") >= 0:
              split = project_name.split("-")
              if len(split) >= 3:
                  app_group = split[0]
                  app_name = split[1]
                  if len(split) == 3:
                      role = split[2]
                  else:
                      role = "-".join(split[2:])
              else:
                  logger.info(
                      f"Unexpected naming format expected <Application Group>-<Application>-<Role> got {project_name}")
          else:
              app_name = project_name
              role = 'Unknown'
              logger.info(
                  f"Unexpected naming format expected <Application Group>-<Application>-<Role> got {project_name}")
      if not app_group:
          if app_name and app_name.find("-") >= 0:
              # backwards compat for the simple days before app groups
              split = app_name.split("-")
              app_group = split[0]
              app_name = split[1]
          else:
              app_group = "Unknown"
      return app_group, app_name, role
  
  
  def _unzip_downloaded_artifacts(dir_with_zips: str, destination: str, logger: Logger) -> str:
      for file in os.listdir(dir_with_zips):
          # expect {tool}-{self.project.name}.zip
          if os.path.basename(file).find("tavern") >= 0:
              shutil.unpack_archive(filename=os.path.join(dir_with_zips, file), extract_dir=f"{destination}/tavern",
                                    format="zip")
          elif os.path.basename(file).find("cypress") >= 0:
              shutil.unpack_archive(filename=os.path.join(dir_with_zips, file), extract_dir=f"{destination}/cypress",
                                    format="zip")
          else:
              logger.warn(f"Unexpected file name in downloaded artifacts {file}")
      return destination
  
  
  def get_project_metadata(logger: Logger, project: Project):
      app_group, app_name, role = extract_application_role(logger, project)
      environment = project.get_mandatory_property(ENVIRONMENT)
      bucket = project.get_property(INTEGRATION_ARTIFACT_BUCKET, f"integration-artifacts-{app_group}-{app_name}")
      return app_group, app_name, bucket, environment, role
  
  
  def get_versioned_artifact_destination(logger, project):
      app_group, app_name, bucket, environment, role = get_project_metadata(logger, project)
      return f"s3://{bucket}/{role}/{project.version}/"
  
  
  def get_latest_artifact_destination(logger, project):
      app_group, app_name, bucket, environment, role = get_project_metadata(logger, project)
      return f"s3://{bucket}/LATEST-{environment}/"
  