text: import os
  import subprocess
  import time
  import yaml
  import json
  import re
  
  import boto3
  from config import DEFAULT_CFG as push_cfg
  import shlex
  from dotenv import load_dotenv
  
  
  # create repository
  def create_ecr_repo(cfg):
      obj = {}
  
      for service_name, service in cfg.services.items():
          if "build" in service:
              # create repository
              ecr_repository_name = f'{cfg.aws_cfg.project.lower()}_{service_name}'
              try:
                  response = client.create_repository(
                      repositoryName=ecr_repository_name,
                      tags=[
                          {
                              'Key': 'platform',
                              'Value': service_name
                          },
                      ]
                  )
  
                  ecr_uri = response['repository']['repositoryUri']
                  obj[ecr_repository_name] = ecr_uri
              except Exception as e:
                  return None
      return obj
  
  
  def tag(cfg):
      for key, value in cfg.ecr_obj.items():
          original_tag = f'{key}:{cfg.tag}-{cfg.deployment_env}'
          new_tag = f'{value}:{cfg.tag}-{cfg.deployment_env}'
          try:
              output = subprocess.check_call(["docker", "tag", original_tag, new_tag])
          except subprocess.CalledProcessError as e:
              print(e.output)
              raise e
  
  
  def push(cfg):
      for key, value in cfg.ecr_obj.items():
          new_tag = f'{value}:{cfg.tag}-{cfg.deployment_env}'
          push = subprocess.Popen(["docker", "push", new_tag])
          status = f"Waiting for {key} push to complete..."
          print(status)
          push.wait()
          print("Done.")
  
  
  def update(service_name, service, ecr_obj):
      for key, value in ecr_obj.items():
          if service_name in key:
              del service["build"]
              service["image"] = value
  
  
  def re_tag_images(cfg):
      if cfg.tag is None:
          raise Exception(f"cfg.tag cannot be None")
      tag(cfg)
  
  
  def push_to_ecr(cfg):
      if cfg.tag is None:
          raise Exception(f"${cfg.tag} cannot be None")
      push(cfg)
  
  
  def update_services_for_deployment(cfg):
      for service_name, service in cfg.services.items():
          service["logging"] = {
              'driver': 'awslogs',
              'options': {
                  'awslogs-group': f"{cfg.aws_cfg.project}",
                  'awslogs-region': f"{cfg.aws_cfg.region}",
                  'awslogs-stream-prefix': f'{cfg.aws_cfg.project}-{service_name}'
              }
          }
          if "build" in service:
              update(service_name, service, cfg.ecr_obj)
  
          if "volumes" in service:
              del service["volumes"]
  
  
  def write_yaml(output_file, yaml_out):
      with open(output_file, "w") as out_file:
          yaml.safe_dump(yaml_out, out_file, default_flow_style=False)
  
      # yaml that is produced is a bit buggy.
      with open(output_file, "r+") as fh:
          lines = map(lambda a: re.sub(r"^\s{4}-", "      -", a), fh.readlines())
  
      with open(output_file, "w") as f:
          f.writelines(lines)
  
      return lines
  
  
  # Write the new docker-compose.yml file.
  def create_deploy_docker_compose_file(cfg):
      write_yaml(cfg.output_file_deploy, cfg.stack)
      print("Wrote new compose file.")
      print(f"COMPOSE_FILE={cfg.output_file_deploy}")
  
  
  def build_exists(cfg, container_name):
      cmd = f"docker inspect --type=image {cfg.aws_cfg.project}_{container_name}:{cfg.tag}-{cfg.deployment_env}"
      try:
          output = subprocess.check_output(shlex.split(cmd)).decode()
      except subprocess.CalledProcessError as e:
          return False
  
      jout = json.loads(output)
      return len(jout) > 0
  
  
  def build(cfg):
      for container_name in cfg.aws_cfg.container_names:
          exists = build_exists(cfg, container_name)
          if not cfg.rebuild and exists:
              continue
          if cfg.rebuild:
              pre_cmd = f"docker-compose" \
                        f" -f {cfg.output_file_build}" \
                        f" --project-directory  {cfg.paipass_dir}" \
                        f" --env-file {cfg.con_env_cfg.env_file}" \
                        f" build"
              cmd = pre_cmd + ' ' + container_name
  
              print(cmd + '\n' * 5, flush=True)
              try:
                  output = subprocess.check_output(shlex.split(cmd))
              except subprocess.CalledProcessError as e:
                  print(e)
                  raise e
  
              else:
                  print(output.decode())
          elif cfg.rebuild and not exists:
              pre_cmd = f"docker build"
              if 'build' in cfg.services[container_name] and 'args' in cfg.services[container_name]['build']:
                  for arg in cfg.services[container_name]['build']['args']:
                      if "${" in arg:
                          arg_resolved = ""
                          var_init = arg.split('=')[0]
                          for request_var_i in arg.split('${')[1:]:
                              request_var_i = request_var_i.split('}')[0]
                              resolved_var = os.getenv(request_var_i)
                              arg_resolved += resolved_var
                          arg = var_init + '=' + arg_resolved
                      pre_cmd += f' --build-arg {arg}'
              pre_cmd += ' '
              cmd = pre_cmd + ' -t ' + f'{cfg.aws_cfg.project}_{container_name}:{cfg.tag}-{cfg.deployment_env}'
              cmd += f' {os.path.join(cfg.paipass_dir, cfg.services[container_name]["build"]["context"])}'
              cmd += '/.'
              print(cmd)
              try:
                  output = subprocess.check_output(shlex.split(cmd))
              except subprocess.CalledProcessError as e:
                  print(e)
                  raise e
  
  
  def update_services_for_build_step(cfg):
      for service_name, service in cfg.services.items():
          if 'image' not in service:
              nombre = f'{cfg.aws_cfg.project}_{service_name}:{cfg.tag}-{cfg.deployment_env}'
              service['image'] = nombre
              service['container_name'] = nombre.split(':')[0]
              print(service['image'])
  
  
  def create_build_docker_compose_file(cfg):
      write_yaml(cfg.output_file_build, cfg.stack)
      print("Wrote new compose file.")
      print(f"COMPOSE_FILE={cfg.output_file_build}")
  
  
  def run(cfg):
      update_services_for_build_step(cfg)
      create_build_docker_compose_file(cfg)
      build(cfg)
      # res = create_ecr_repo(cfg)
      re_tag_images(cfg)
      push_to_ecr(cfg)
      update_services_for_deployment(cfg)
  
      create_deploy_docker_compose_file(cfg)
  
  
  if __name__ == '__main__':
      import argparse
  
      # underscores are for refactoring needs (specifically, to make old stuff break due to new names).
  
      _deployment_env = 'staging'
      _docker_tag_name = 'paimsgs1'
      # use if repository exist
      push_cfg.tag = _docker_tag_name
      push_cfg.deployment_env = _deployment_env
      push_cfg.rebuild = True
      client = boto3.client('ecr',
                            region_name=push_cfg.aws_cfg.region,
                            aws_access_key_id=push_cfg.aws_cfg.access_key,
                            aws_secret_access_key=push_cfg.aws_cfg.secret_access_key, )
  
      # Generate version number for build
      _version = str(int(time.time()))
  
      _alt_input = f"docker-compose.{push_cfg.deployment_env}.yml"
      _alt_output_deploy = f"docker-compose.{push_cfg.deployment_env}.deploy.yml-{_version}"
      _alt_output_build = f"docker-compose.{push_cfg.deployment_env}.build.yml-{_version}"
  
      _input_file = os.environ.get("DOCKER_COMPOSE_YML_INPUT", _alt_input)
      _output_file_deploy = os.environ.get("DOCKER_COMPOSE_YML_OUTPUT_DEPLOY", _alt_output_deploy)
      _output_file_build = os.environ.get("DOCKER_COMPOSE_YML_OUTPUT_BUILD", _alt_output_build)
  
      if _input_file == _output_file_deploy == _output_file_build == "docker-compose.yml":
          print("I will not clobber your docker-compose.yml file.")
          print("Please unset DOCKER_COMPOSE_YML or set it to something else.")
          exit(1)
  
      push_cfg.input_file = _input_file
      push_cfg.output_file_deploy = _output_file_deploy
      push_cfg.output_file_build = os.path.abspath(_output_file_build)
  
      with open(_input_file) as f:
          _stack = yaml.safe_load(f)
      push_cfg.stack = _stack
      push_cfg.services = _stack["services"]
  
      parser = argparse.ArgumentParser()
      parser.add_argument('--dep-env')
      parser.add_argument('--env-file')
      parser.add_argument('--tag')
      args = parser.parse_args(f'--dep-env staging '
                               f' --env-file ~/catena/staging.env '
                               f' --tag {push_cfg.tag}'.split())
  
      push_cfg.con_env_cfg.env_file = os.path.expanduser(args.env_file)
      # load env file into system so that os.getenv works
      load_dotenv(push_cfg.con_env_cfg.env_file)
      # update_task_def('backend', push_cfg.deployment_env, push_cfg.con_env_cfg.env_file)
      push_cfg
      run(push_cfg)
  