From ac0eb23815aacebe518a2eb6be06d971f5c00df1 Mon Sep 17 00:00:00 2001
From: Ali <>
Date: Thu, 11 Aug 2022 00:39:11 +0400
Subject: [PATCH] Working state

---
 build-system/Make/BuildConfiguration.py |  65 +++++--
 build-system/Make/ImportCertificates.py |   2 +-
 build-system/Make/Make.py               | 160 +++++++++++-----
 build-system/Make/RemoteBuild.py        |  57 +++---
 buildbox/build.py                       | 244 ------------------------
 5 files changed, 192 insertions(+), 336 deletions(-)
 delete mode 100644 buildbox/build.py

diff --git a/build-system/Make/BuildConfiguration.py b/build-system/Make/BuildConfiguration.py
index 463a44c8e0..3029f6bf08 100644
--- a/build-system/Make/BuildConfiguration.py
+++ b/build-system/Make/BuildConfiguration.py
@@ -112,7 +112,7 @@ def decrypt_codesigning_directory_recursively(source_base_path, destination_base
             decrypt_codesigning_directory_recursively(source_path, destination_path, password)
 
 
-def load_provisioning_profiles_from_git(working_dir, repo_url, branch, password, always_fetch):
+def load_codesigning_data_from_git(working_dir, repo_url, branch, password, always_fetch):
     if not os.path.exists(working_dir):
         os.makedirs(working_dir, exist_ok=True)
 
@@ -121,15 +121,15 @@ def load_provisioning_profiles_from_git(working_dir, repo_url, branch, password,
         if always_fetch:
             original_working_dir = os.getcwd()
             os.chdir(encrypted_working_dir)
-            os.system('git fetch')
+            os.system('GIT_SSH_COMMAND="ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" git fetch')
             os.system('git checkout "{branch}"'.format(branch=branch))
-            os.system('git pull')
+            os.system('GIT_SSH_COMMAND="ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" git pull')
             os.chdir(original_working_dir)
     else:
         os.makedirs(encrypted_working_dir, exist_ok=True)
         original_working_dir = os.getcwd()
         os.chdir(working_dir)
-        os.system('git clone {repo_url} -b "{branch}" "{target_path}"'.format(
+        os.system('git -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null clone {repo_url} -b "{branch}" "{target_path}"'.format(
             repo_url=repo_url,
             branch=branch,
             target_path=encrypted_working_dir
@@ -185,42 +185,67 @@ def copy_profiles_from_directory(source_path, destination_path, team_id, bundle_
                     print('Warning: skipping provisioning profile at {} with bundle_id {} (base_name {})'.format(file_path, profile_name, profile_base_name))
 
 
-class ProvisioningProfileSource:
+def copy_certificates_from_directory(source_path, destination_path):
+    for file_name in os.listdir(source_path):
+        file_path = source_path + '/' + file_name
+        if os.path.isfile(file_path):
+            if file_path.endswith('.p12') or file_path.endswith('.cer'):
+                shutil.copyfile(file_path, destination_path + '/' + file_name)
+
+
+class CodesigningSource:
     def __init__(self):
         pass
 
+    def load_data(self, working_dir):
+        raise Exception('Not implemented')
+
     def copy_profiles_to_destination(self, destination_path):
         raise Exception('Not implemented')
 
+    def copy_certificates_to_destination(self, destination_path):
+        raise Exception('Not implemented')
 
-class GitProvisioningProfileSource(ProvisioningProfileSource):
-    def __init__(self, working_dir, repo_url, team_id, bundle_id, profile_type, password, always_fetch):
-        self.working_dir = working_dir
+
+class GitCodesigningSource(CodesigningSource):
+    def __init__(self, repo_url, team_id, bundle_id, codesigning_type, password, always_fetch):
         self.repo_url = repo_url
         self.team_id = team_id
         self.bundle_id = bundle_id
-        self.profile_type = profile_type
+        self.codesigning_type = codesigning_type
         self.password = password
         self.always_fetch = always_fetch
 
+    def load_data(self, working_dir):
+        self.working_dir = working_dir
+        load_codesigning_data_from_git(working_dir=self.working_dir, repo_url=self.repo_url, branch=self.team_id, password=self.password, always_fetch=self.always_fetch)
+
     def copy_profiles_to_destination(self, destination_path):
-        load_provisioning_profiles_from_git(working_dir=self.working_dir, repo_url=self.repo_url, branch=self.team_id, password=self.password, always_fetch=self.always_fetch)
-        copy_profiles_from_directory(source_path=self.working_dir + '/decrypted/profiles/{}'.format(self.profile_type), destination_path=destination_path, team_id=self.team_id, bundle_id=self.bundle_id)
+        source_path = self.working_dir + '/decrypted/profiles/{}'.format(self.codesigning_type)
+        copy_profiles_from_directory(source_path=source_path, destination_path=destination_path, team_id=self.team_id, bundle_id=self.bundle_id)
+
+    def copy_certificates_to_destination(self, destination_path):
+        source_path = None
+        if self.codesigning_type in ['adhoc', 'appstore', 'enterprise']:
+            source_path = self.working_dir + '/decrypted/certs/distribution'
+        elif self.codesigning_type == 'development':
+            source_path = self.working_dir + '/decrypted/certs/development'
+        else:
+            raise Exception('Unknown codesigning type {}'.format(self.codesigning_type))
+        copy_certificates_from_directory(source_path=source_path, destination_path=destination_path)
 
 
-class DirectoryProvisioningProfileSource(ProvisioningProfileSource):
+class DirectoryCodesigningSource(CodesigningSource):
     def __init__(self, directory_path, team_id, bundle_id):
         self.directory_path = directory_path
         self.team_id = team_id
         self.bundle_id = bundle_id
 
+    def load_data(self, working_dir):
+        pass
+
     def copy_profiles_to_destination(self, destination_path):
-        profiles_path = self.directory_path
-        if not os.path.exists(profiles_path):
-            print('{} does not exist'.format(profiles_path))
-            sys.exit(1)
-        copy_profiles_from_directory(source_path=profiles_path, destination_path=destination_path, team_id=self.team_id, bundle_id=self.bundle_id)
+        copy_profiles_from_directory(source_path=self.directory_path + '/profiles', destination_path=destination_path, team_id=self.team_id, bundle_id=self.bundle_id)
 
-
-def generate_configuration_repository(path, profile_source):
-    pass
+    def copy_certificates_to_destination(self, destination_path):
+        copy_certificates_from_directory(source_path=self.directory_path + '/certs', destination_path=destination_path)
diff --git a/build-system/Make/ImportCertificates.py b/build-system/Make/ImportCertificates.py
index 2e6ddd6a1e..c449fe4b5e 100644
--- a/build-system/Make/ImportCertificates.py
+++ b/build-system/Make/ImportCertificates.py
@@ -40,7 +40,7 @@ def import_certificates(certificatesPath):
 
     for file_name in os.listdir(certificatesPath):
         file_path = certificatesPath + '/' + file_name
-        if file_path.endwith('.p12') or file_path.endwith('.cer'):
+        if file_path.endswith('.p12') or file_path.endswith('.cer'):
             run_executable_with_output('security', arguments=[
                 'import',
                 file_path,
diff --git a/build-system/Make/Make.py b/build-system/Make/Make.py
index f3e5c3ed19..a392e31a71 100644
--- a/build-system/Make/Make.py
+++ b/build-system/Make/Make.py
@@ -7,11 +7,12 @@ import sys
 import tempfile
 import subprocess
 import shutil
+import glob
 
-from BuildEnvironment import resolve_executable, call_executable, BuildEnvironment
+from BuildEnvironment import resolve_executable, call_executable, run_executable_with_output, BuildEnvironment
 from ProjectGeneration import generate
 from BazelLocation import locate_bazel
-from BuildConfiguration import ProvisioningProfileSource, GitProvisioningProfileSource, DirectoryProvisioningProfileSource, BuildConfiguration, build_configuration_from_json
+from BuildConfiguration import CodesigningSource, GitCodesigningSource, DirectoryCodesigningSource, BuildConfiguration, build_configuration_from_json
 import RemoteBuild
 
 class BazelCommandLine:
@@ -383,7 +384,7 @@ def clean(bazel, arguments):
     bazel_command_line.invoke_clean()
 
 
-def resolve_codesigning(arguments, base_path, build_configuration, certificates_path, provisioning_profiles_path):
+def resolve_codesigning(arguments, base_path, build_configuration, provisioning_profiles_path, additional_codesigning_output_path):
     profile_source = None
     if arguments.gitCodesigningRepository is not None:
         password = os.getenv('TELEGRAM_CODESIGNING_GIT_PASSWORD')
@@ -395,31 +396,36 @@ def resolve_codesigning(arguments, base_path, build_configuration, certificates_
             print('--gitCodesigningType is required if --gitCodesigningRepository is set')
             sys.exit(1)
 
-        workdir_path = '{}/build-input/configuration-repository-workdir'.format(base_path)
-        os.makedirs(workdir_path, exist_ok=True)
-
-        profile_source = GitProvisioningProfileSource(
-            working_dir=workdir_path,
+        profile_source = GitCodesigningSource(
             repo_url=arguments.gitCodesigningRepository,
             team_id=build_configuration.team_id,
             bundle_id=build_configuration.bundle_id,
-            profile_type=arguments.gitCodesigningType,
+            codesigning_type=arguments.gitCodesigningType,
             password=password,
             always_fetch=arguments.gitCodesigningAlwaysFetch
         )
-    elif arguments.provisioningProfilesPath is not None:
-        profile_source = DirectoryProvisioningProfileSource(
-            directory_path=arguments.provisioningProfilesPath,
+    elif arguments.codesigningInformationPath is not None:
+        profile_source = DirectoryCodesigningSource(
+            directory_path=arguments.codesigningInformationPath,
             team_id=build_configuration.team_id,
             bundle_id=build_configuration.bundle_id
         )
     else:
-        raise Exception('Neither gitCodesigningRepository nor provisioningProfilesPath are set')
+        raise Exception('Neither gitCodesigningRepository nor codesigningInformationPath are set')
 
-    profile_source.copy_profiles_to_destination(destination_path=provisioning_profiles_path)
+    workdir_path = '{}/build-input/configuration-repository-workdir'.format(base_path)
+    os.makedirs(workdir_path, exist_ok=True)
+    profile_source.load_data(working_dir=workdir_path)
+
+    if provisioning_profiles_path is not None:
+        profile_source.copy_profiles_to_destination(destination_path=provisioning_profiles_path)
+
+    if additional_codesigning_output_path is not None:
+        profile_source.copy_profiles_to_destination(destination_path=additional_codesigning_output_path + '/profiles')
+        profile_source.copy_certificates_to_destination(destination_path=additional_codesigning_output_path + '/certs')
 
 
-def resolve_configuration(base_path, bazel_command_line: BazelCommandLine, arguments, aps_environment):
+def resolve_configuration(base_path, bazel_command_line: BazelCommandLine, arguments, aps_environment, additional_codesigning_output_path):
     configuration_repository_path = '{}/build-input/configuration-repository'.format(base_path)
     os.makedirs(configuration_repository_path, exist_ok=True)
 
@@ -438,7 +444,13 @@ def resolve_configuration(base_path, bazel_command_line: BazelCommandLine, argum
         shutil.rmtree(provisioning_path)
     os.makedirs(provisioning_path, exist_ok=True)
 
-    resolve_codesigning(arguments=arguments, base_path=base_path, build_configuration=build_configuration, certificates_path=None, provisioning_profiles_path=provisioning_path)
+    resolve_codesigning(
+        arguments=arguments,
+        base_path=base_path,
+        build_configuration=build_configuration,
+        provisioning_profiles_path=provisioning_path,
+        additional_codesigning_output_path=additional_codesigning_output_path
+    )
 
     provisioning_profile_files = []
     for file_name in os.listdir(provisioning_path):
@@ -451,7 +463,8 @@ def resolve_configuration(base_path, bazel_command_line: BazelCommandLine, argum
             file.write('    "{}",\n'.format(file_name))
         file.write('])\n')
 
-    return configuration_repository_path
+    if bazel_command_line is not None:
+        bazel_command_line.set_configuration_path(configuration_repository_path)
 
 
 def generate_project(bazel, arguments):
@@ -469,10 +482,13 @@ def generate_project(bazel, arguments):
 
     bazel_command_line.set_continue_on_error(arguments.continueOnError)
 
-    configuration_repository_path = resolve_configuration(base_path=os.getcwd(), bazel_command_line=bazel_command_line, arguments=arguments, aps_environment=arguments.apsEnvironment)
-
-    if bazel_command_line is not None:
-        bazel_command_line.set_configuration_path(configuration_repository_path)
+    resolve_configuration(
+        base_path=os.getcwd(),
+        bazel_command_line=bazel_command_line,
+        arguments=arguments,
+        aps_environment=arguments.apsEnvironment,
+        additional_codesigning_output_path=None
+    )
 
     bazel_command_line.set_build_number(arguments.buildNumber)
 
@@ -516,7 +532,13 @@ def build(bazel, arguments):
     elif arguments.cacheHost is not None:
         bazel_command_line.add_remote_cache(arguments.cacheHost)
 
-    resolve_configuration(base_path=os.getcwd(), bazel_command_line=bazel_command_line, arguments=arguments, aps_environment=arguments.apsEnvironment)
+    resolve_configuration(
+        base_path=os.getcwd(),
+        bazel_command_line=bazel_command_line,
+        arguments=arguments,
+        aps_environment=arguments.apsEnvironment,
+        additional_codesigning_output_path=None
+    )
 
     bazel_command_line.set_configuration(arguments.configuration)
     bazel_command_line.set_build_number(arguments.buildNumber)
@@ -528,6 +550,36 @@ def build(bazel, arguments):
 
     bazel_command_line.invoke_build()
 
+    if arguments.outputBuildArtifactsPath is not None:
+        artifacts_path = arguments.outputBuildArtifactsPath
+        if os.path.exists(artifacts_path + '/Telegram.ipa'):
+            os.remove(path)
+        if os.path.exists(artifacts_path + '/DSYMs'):
+            shutil.rmtree(artifacts_path + '/DSYMs')
+        os.makedirs(artifacts_path, exist_ok=True)
+        os.makedirs(artifacts_path + '/DSYMs', exist_ok=True)
+
+        ipa_paths = glob.glob('bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/Telegram.ipa')
+        if len(ipa_paths) == 0:
+            print('Could not find the IPA at bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/Telegram.ipa')
+            sys.exit(1)
+        elif len(ipa_paths) > 1:
+            print('Multiple matching IPA files found: {}'.format(ipa_paths))
+            sys.exit(1)
+        shutil.copyfile(ipa_paths[0], artifacts_path + '/Telegram.ipa')
+
+        dsym_paths = glob.glob('bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/*.dSYM')
+        for dsym_path in dsym_paths:
+            file_name = os.path.basename(dsym_path)
+            shutil.copyfile(ipa_paths[0], artifacts_path + '/DSYMs/{}'.format(file_name))
+        run_executable_with_output('zip', arguments=[
+            '-9',
+            '-r',
+            artifacts_path + '/Telegram.DSYMs.zip',
+            artifacts_path + '/DSYMs'
+        ], check_result=True)
+        shutil.rmtree(artifacts_path + '/DSYMs')
+
 
 def test(bazel, arguments):
     bazel_command_line = BazelCommandLine(
@@ -542,7 +594,13 @@ def test(bazel, arguments):
     elif arguments.cacheHost is not None:
         bazel_command_line.add_remote_cache(arguments.cacheHost)
 
-    resolve_configuration(base_path=os.getcwd(), bazel_command_line=bazel_command_line, arguments=arguments, aps_environment=arguments.apsEnvironment)
+    resolve_configuration(
+        base_path=os.getcwd(),
+        bazel_command_line=bazel_command_line,
+        arguments=arguments,
+        aps_environment=arguments.apsEnvironment,
+        additional_codesigning_output_path=None
+    )
 
     bazel_command_line.set_configuration('debug_sim_arm64')
     bazel_command_line.set_build_number('10000')
@@ -562,13 +620,6 @@ def add_codesigning_common_arguments(current_parser: argparse.ArgumentParser):
     )
 
     codesigning_group = current_parser.add_mutually_exclusive_group(required=True)
-    codesigning_group.add_argument(
-        '--reproducibleCodesigning',
-        action='store_true',
-        help='''
-            Use locally generated provisioning profiles and certificates for a reproducible build.
-            '''
-    )
     codesigning_group.add_argument(
         '--gitCodesigningRepository',
         help='''
@@ -578,15 +629,21 @@ def add_codesigning_common_arguments(current_parser: argparse.ArgumentParser):
         metavar='path'
     )
     codesigning_group.add_argument(
-        '--provisioningProfilesPath',
+        '--codesigningInformationPath',
         help='''
-            Use provisioning profiles from a local directory.
+            Use signing certificates and provisioning profiles from a local directory.
             ''',
         metavar='command'
     )
 
     current_parser.add_argument(
         '--gitCodesigningType',
+        choices=[
+            'development',
+            'adhoc',
+            'appstore',
+            'enterprise'
+        ],
         required=False,
         help='''
             The name of the folder to use inside "profiles" folder in the git repository.
@@ -793,6 +850,12 @@ if __name__ == '__main__':
         default=False,
         help='Enable sandbox.',
     )
+    buildParser.add_argument(
+        '--outputBuildArtifactsPath',
+        required=False,
+        help='Store IPA and DSYM at the specified path after a successful build.',
+        metavar='arguments'
+    )
 
     remote_build_parser = subparsers.add_parser('remote-build', help='Build the app using a remote environment.')
     add_codesigning_common_arguments(remote_build_parser)
@@ -802,7 +865,7 @@ if __name__ == '__main__':
         type=str,
         help='DarwinContainers host address.'
     )
-    buildParser.add_argument(
+    remote_build_parser.add_argument(
         '--configuration',
         choices=[
             'debug_universal',
@@ -816,7 +879,7 @@ if __name__ == '__main__':
         help='Build configuration'
     )
     remote_build_parser.add_argument(
-        '--bazelCacheHost',
+        '--cacheHost',
         required=False,
         type=str,
         help='Bazel remote cache host address.'
@@ -850,24 +913,27 @@ if __name__ == '__main__':
         elif args.commandName == 'remote-build':
             base_path = os.getcwd()
             remote_input_path = '{}/build-input/remote-input'.format(base_path)
-            certificates_path = '{}/certs'.format(remote_input_path)
-            provisioning_profiles_path = '{}/profiles'.format(remote_input_path)
+            if os.path.exists(remote_input_path):
+                shutil.rmtree(remote_input_path)
+            os.makedirs(remote_input_path)
+            os.makedirs(remote_input_path + '/certs')
+            os.makedirs(remote_input_path + '/profiles')
 
-            os.makedirs(certificates_path, exist_ok=True)
-            os.makedirs(provisioning_profiles_path, exist_ok=True)
-
-            configuration_repository_path = resolve_configuration(base_path=os.getcwd(), bazel_command_line=None, arguments=arguments, aps_environment='production')
-
-            certificates_path = '{}/certs'.format(configuration_repository_path)
-            provisioning_profiles_path = '{}/profiles'.format(configuration_repository_path)
+            resolve_configuration(
+                base_path=os.getcwd(),
+                bazel_command_line=None,
+                arguments=args,
+                aps_environment='production',
+                additional_codesigning_output_path=remote_input_path
+            )
+            
+            shutil.copyfile(args.configurationPath, remote_input_path + '/configuration.json')
 
             RemoteBuild.remote_build(
                 darwin_containers_host=args.darwinContainersHost,
-                bazel_cache_host=args.bazelCacheHost,
+                bazel_cache_host=args.cacheHost,
                 configuration=args.configuration,
-                certificates_path=certificates_path,
-                provisioning_profiles_path=provisioning_profiles_path,
-                configurationPath=args.configurationPath
+                build_input_data_path=remote_input_path
             )
         elif args.commandName == 'test':
             test(bazel=bazel_path, arguments=args)
diff --git a/build-system/Make/RemoteBuild.py b/build-system/Make/RemoteBuild.py
index e7bb7f2e6c..bdca64501b 100644
--- a/build-system/Make/RemoteBuild.py
+++ b/build-system/Make/RemoteBuild.py
@@ -35,7 +35,9 @@ def session_ssh(session, command):
     )
     return os.system(ssh_command)
 
-def remote_build(darwin_containers_host, bazel_cache_host, configuration, certificates_path, provisioning_profiles_path, configurationPath):
+def remote_build(darwin_containers_host, bazel_cache_host, configuration, build_input_data_path):
+    macos_version = '12.5'
+
     from darwin_containers import DarwinContainers
 
     base_dir = os.getcwd()
@@ -63,7 +65,6 @@ def remote_build(darwin_containers_host, bazel_cache_host, configuration, certif
     build_number = build_number_offset + int(commit_count)
     print('Build number: {}'.format(build_number))
 
-    macos_version = '12.5'
     image_name = 'macos-{macos_version}-xcode-{xcode_version}'.format(macos_version=macos_version, xcode_version=xcode_version)
 
     print('Image name: {}'.format(image_name))
@@ -83,29 +84,34 @@ def remote_build(darwin_containers_host, bazel_cache_host, configuration, certif
     print('Opening container session...')
     with darwinContainers.workingImageSession(name=image_name) as session:
         print('Uploading data to container...')
-        session_scp_upload(session=session, source_path=certificates_path, destination_path='certs')
-        session_scp_upload(session=session, source_path=provisioning_profiles_path, destination_path='profiles')
-        session_scp_upload(session=session, source_path=configurationPath, destination_path='configuration.json')
+        session_scp_upload(session=session, source_path=build_input_data_path, destination_path='telegram-build-input')
         session_scp_upload(session=session, source_path='{base_dir}/{buildbox_dir}/transient-data/source.tar'.format(base_dir=base_dir, buildbox_dir=buildbox_dir), destination_path='')
 
         guest_build_sh = '''
-            mkdir telegram-ios
-            cd telegram-ios
-            tar -xf ../source.tar
+            set -x
+            set -e
 
-            python3 build-system/Make/ImportCertificates.py --path $HOME/certs
+            mkdir /Users/Shared/telegram-ios
+            cd /Users/Shared/telegram-ios
+
+            tar -xf $HOME/source.tar
+
+            python3 build-system/Make/ImportCertificates.py --path $HOME/telegram-build-input/certs
+
+        '''
+
+        guest_build_sh += 'python3 build-system/Make/Make.py \\'
+        if bazel_cache_host is not None:
+            guest_build_sh += '--cacheHost="{}" \\'.format(bazel_cache_host)
+        guest_build_sh += 'build \\'
+        guest_build_sh += ''
+        guest_build_sh += '--buildNumber={} \\'.format(build_number)
+        guest_build_sh += '--configuration={} \\'.format(configuration)
+        guest_build_sh += '--configurationPath=$HOME/telegram-build-input/configuration.json \\'
+        guest_build_sh += '--codesigningInformationPath=$HOME/telegram-build-input \\'
+        guest_build_sh += '--apsEnvironment=production \\'
+        guest_build_sh += '--outputBuildArtifactsPath=/Users/Shared/telegram-ios/build/artifacts \\'
 
-            python3 build-system/Make/Make.py \\
-                build \\
-                --buildNumber={build_number} \\
-                --configuration={configuration} \\
-                --configurationPath=$HOME/configuration.json \\
-                --apsEnvironment=production \\
-                --provisioningProfilesPath=$HOME/profiles
-        '''.format(
-            build_number=build_number,
-            configuration=configuration
-        )
         guest_build_file_path = tempfile.mktemp()
         with open(guest_build_file_path, 'w+') as file:
             file.write(guest_build_sh)
@@ -114,8 +120,6 @@ def remote_build(darwin_containers_host, bazel_cache_host, configuration, certif
 
         print('Executing remote build...')
 
-        if bazel_cache_host is None:
-            bazel_cache_host = ''
         session_ssh(session=session, command='bash -l guest-build-telegram.sh')
 
         print('Retrieving build artifacts...')
@@ -125,5 +129,10 @@ def remote_build(darwin_containers_host, bazel_cache_host, configuration, certif
             shutil.rmtree(artifacts_path)
         os.makedirs(artifacts_path, exist_ok=True)
 
-        session_scp_download(session=session, source_path='telegram-ios/build/artifacts/*', destination_path='{artifacts_path}/'.format(artifacts_path=artifacts_path))
-        print('Artifacts have been stored at {}'.format(artifacts_path))
+        session_scp_download(session=session, source_path='/Users/Shared/telegram-ios/build/artifacts/*', destination_path='{artifacts_path}/'.format(artifacts_path=artifacts_path))
+
+        if os.path.exists(artifacts_path + '/Telegram.ipa'):
+            print('Artifacts have been stored at {}'.format(artifacts_path))
+        else:
+            print('Telegram.ipa not found')
+            sys.exit(1)
diff --git a/buildbox/build.py b/buildbox/build.py
deleted file mode 100644
index 151967507e..0000000000
--- a/buildbox/build.py
+++ /dev/null
@@ -1,244 +0,0 @@
-#!/usr/bin/python3
-
-import argparse
-import json
-import os
-import sys
-import shlex
-import shutil
-import subprocess
-import time
-import pipes
-import tempfile
-
-def quote_args(seq):
-	return ' '.join(pipes.quote(arg) for arg in seq)
-
-def get_clean_env():
-    clean_env = os.environ.copy()
-    return clean_env
-
-def resolve_executable(program):
-	def is_executable(fpath):
-		return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
-
-	for path in get_clean_env()["PATH"].split(os.pathsep):
-		executable_file = os.path.join(path, program)
-		if is_executable(executable_file):
-			return executable_file
-	return None
-
-
-def run_executable_with_output(path, arguments, check_result=False):
-	executable_path = resolve_executable(path)
-	if executable_path is None:
-		raise Exception('Could not resolve {} to a valid executable file'.format(path))
-
-	process = subprocess.Popen(
-		[executable_path] + arguments,
-		stdout=subprocess.PIPE,
-		stderr=subprocess.STDOUT,
-		env=get_clean_env()
-	)
-	output_data, _ = process.communicate()
-	output_string = output_data.decode('utf-8')
-
-	if check_result:
-		if process.returncode != 0:
-			print('Command {} {} finished with non-zero return code and output:\n{}'.format(executable_path, arguments, output_string))
-			sys.exit(1)
-
-	return output_string
-
-
-
-
-def isolated_build(arguments):
-	if arguments.certificatesPath is not None:
-		if not os.path.exists(arguments.certificatesPath):
-			print('{} does not exist'.format(arguments.certificatesPath))
-			sys.exit(1)
-
-		keychain_name = 'temp.keychain'
-		keychain_password = 'secret'
-
-		existing_keychains = run_executable_with_output('security', arguments=['list-keychains'], check_result=True)
-		if keychain_name in existing_keychains:
-			run_executable_with_output('security', arguments=['delete-keychain'], check_result=True)
-
-		run_executable_with_output('security', arguments=[
-			'create-keychain',
-			'-p',
-			keychain_password,
-			keychain_name
-		], check_result=True)
-
-		existing_keychains = run_executable_with_output('security', arguments=['list-keychains', '-d', 'user'])
-		existing_keychains.replace('"', '')
-
-		run_executable_with_output('security', arguments=[
-			'list-keychains',
-			'-d',
-			'user',
-			'-s',
-			keychain_name,
-			existing_keychains
-		], check_result=True)
-
-		run_executable_with_output('security', arguments=['set-keychain-settings', keychain_name])
-		run_executable_with_output('security', arguments=['unlock-keychain', '-p', keychain_password, keychain_name])
-
-		for file_name in os.listdir(arguments.certificatesPath):
-			file_path = arguments.certificatesPath + '/' + file_name
-			if file_path.endwith('.p12') or file_path.endwith('.cer'):
-				run_executable_with_output('security', arguments=[
-					'import',
-					file_path,
-					'-k',
-					keychain_name,
-					'-P',
-					'',
-					'-T',
-					'/usr/bin/codesign',
-					'-T',
-					'/usr/bin/security'
-				], check_result=True)
-
-		run_executable_with_output('security', arguments=[
-			'import',
-			'build-system/AppleWWDRCAG3.cer',
-			'-k',
-			keychain_name,
-			'-P',
-			'',
-			'-T',
-			'/usr/bin/codesign',
-			'-T',
-			'/usr/bin/security'
-		], check_result=True)
-
-		run_executable_with_output('security', arguments=[
-			'set-key-partition-list',
-			'-S',
-			'apple-tool:,apple:',
-			'-k',
-			keychain_password,
-			keychain_name
-		], check_result=True)
-
-	build_arguments = ['build-system/Make/Make.py']
-	
-	#build_arguments.append('--bazel="$(pwd)/tools/bazel"')
-	
-	if arguments.cacheHost is not None:
-		build_arguments.append('--cacheHost={}'.format(arguments.cacheHost))
-
-	build_arguments.append('build')
-
-	build_arguments.append('--configurationPath={}'.format(arguments.configurationPath))
-	build_arguments.append('--buildNumber={}'.format(arguments.buildNumber))
-	build_arguments.append('--configuration={}'.format(arguments.configuration))
-	build_arguments.append('--apsEnvironment=production')
-	build_arguments.append('--disableParallelSwiftmoduleGeneration')
-	build_arguments.append('--provisioningProfilesPath={}'.format(arguments.provisioningProfilesPath))
-
-	build_command = 'python3 ' + quote_args(build_arguments)
-	print('Running {}'.format(build_command))
-	os.system(build_command)
-
-
-if __name__ == '__main__':
-	parser = argparse.ArgumentParser(prog='build')
-
-	parser.add_argument(
-		'--verbose',
-		action='store_true',
-		default=False,
-		help='Print debug info'
-	)
-
-	subparsers = parser.add_subparsers(dest='commandName', help='Commands')
-
-	remote_build_parser = subparsers.add_parser('remote-build', help='Build the app using a remote environment.')
-	remote_build_parser.add_argument(
-		'--darwinContainersHost',
-		required=True,
-		type=str,
-		help='DarwinContainers host address.'
-	)
-	remote_build_parser.add_argument(
-		'--configuration',
-		choices=[
-			'appcenter',
-			'appstore',
-			'reproducible'
-		],
-		required=True,
-		help='Build configuration'
-	)
-	remote_build_parser.add_argument(
-		'--bazelCacheHost',
-		required=False,
-		type=str,
-		help='Bazel remote cache host address.'
-	)
-
-	isolated_build_parser = subparsers.add_parser('isolated-build', help='Build the app inside an isolated environment.')
-	isolated_build_parser.add_argument(
-		'--certificatesPath',
-		required=False,
-		type=str,
-		help='Install codesigning certificates from the specified directory.'
-	)
-	isolated_build_parser.add_argument(
-		'--provisioningProfilesPath',
-		required=True,
-		help='''
-			Use codesigning provisioning profiles from a local directory.
-			''',
-		metavar='command'
-	)
-	isolated_build_parser.add_argument(
-		'--cacheHost',
-		required=False,
-		type=str,
-		help='Bazel cache host url.'
-	)
-	isolated_build_parser.add_argument(
-		'--configurationPath',
-		help='''
-			Path to a json containing build configuration.
-			See build-system/appstore-configuration.json for an example.
-			''',
-		required=True,
-		metavar='path'
-	)
-	isolated_build_parser.add_argument(
-		'--buildNumber',
-		required=True,
-		type=int,
-		help='Build number.',
-		metavar='number'
-	)
-	isolated_build_parser.add_argument(
-		'--configuration',
-		type=str,
-		required=True,
-		help='Build configuration'
-	)
-
-	if len(sys.argv) < 2:
-		parser.print_help()
-		sys.exit(1)
-
-	args = parser.parse_args()
-
-	if args.commandName is None:
-		exit(0)
-
-	if args.commandName == 'remote-build':
-		remote_build(darwin_containers_host=args.darwinContainersHost, bazel_cache_host=args.bazelCacheHost, configuration=args.configuration)
-	elif args.commandName == 'isolated-build':
-		isolated_build(arguments=args)
-
-