Working state

This commit is contained in:
Ali 2022-08-11 00:39:11 +04:00
parent 1965e01680
commit ac0eb23815
5 changed files with 192 additions and 336 deletions

View File

@ -112,7 +112,7 @@ def decrypt_codesigning_directory_recursively(source_base_path, destination_base
decrypt_codesigning_directory_recursively(source_path, destination_path, password) 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): if not os.path.exists(working_dir):
os.makedirs(working_dir, exist_ok=True) 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: if always_fetch:
original_working_dir = os.getcwd() original_working_dir = os.getcwd()
os.chdir(encrypted_working_dir) 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 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) os.chdir(original_working_dir)
else: else:
os.makedirs(encrypted_working_dir, exist_ok=True) os.makedirs(encrypted_working_dir, exist_ok=True)
original_working_dir = os.getcwd() original_working_dir = os.getcwd()
os.chdir(working_dir) 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, repo_url=repo_url,
branch=branch, branch=branch,
target_path=encrypted_working_dir 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)) 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): def __init__(self):
pass pass
def load_data(self, working_dir):
raise Exception('Not implemented')
def copy_profiles_to_destination(self, destination_path): def copy_profiles_to_destination(self, destination_path):
raise Exception('Not implemented') 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): class GitCodesigningSource(CodesigningSource):
self.working_dir = working_dir def __init__(self, repo_url, team_id, bundle_id, codesigning_type, password, always_fetch):
self.repo_url = repo_url self.repo_url = repo_url
self.team_id = team_id self.team_id = team_id
self.bundle_id = bundle_id self.bundle_id = bundle_id
self.profile_type = profile_type self.codesigning_type = codesigning_type
self.password = password self.password = password
self.always_fetch = always_fetch 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): 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) source_path = self.working_dir + '/decrypted/profiles/{}'.format(self.codesigning_type)
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) 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): def __init__(self, directory_path, team_id, bundle_id):
self.directory_path = directory_path self.directory_path = directory_path
self.team_id = team_id self.team_id = team_id
self.bundle_id = bundle_id self.bundle_id = bundle_id
def load_data(self, working_dir):
pass
def copy_profiles_to_destination(self, destination_path): def copy_profiles_to_destination(self, destination_path):
profiles_path = self.directory_path copy_profiles_from_directory(source_path=self.directory_path + '/profiles', destination_path=destination_path, team_id=self.team_id, bundle_id=self.bundle_id)
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)
def copy_certificates_to_destination(self, destination_path):
def generate_configuration_repository(path, profile_source): copy_certificates_from_directory(source_path=self.directory_path + '/certs', destination_path=destination_path)
pass

View File

@ -40,7 +40,7 @@ def import_certificates(certificatesPath):
for file_name in os.listdir(certificatesPath): for file_name in os.listdir(certificatesPath):
file_path = certificatesPath + '/' + file_name 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=[ run_executable_with_output('security', arguments=[
'import', 'import',
file_path, file_path,

View File

@ -7,11 +7,12 @@ import sys
import tempfile import tempfile
import subprocess import subprocess
import shutil 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 ProjectGeneration import generate
from BazelLocation import locate_bazel 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 import RemoteBuild
class BazelCommandLine: class BazelCommandLine:
@ -383,7 +384,7 @@ def clean(bazel, arguments):
bazel_command_line.invoke_clean() 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 profile_source = None
if arguments.gitCodesigningRepository is not None: if arguments.gitCodesigningRepository is not None:
password = os.getenv('TELEGRAM_CODESIGNING_GIT_PASSWORD') 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') print('--gitCodesigningType is required if --gitCodesigningRepository is set')
sys.exit(1) sys.exit(1)
workdir_path = '{}/build-input/configuration-repository-workdir'.format(base_path) profile_source = GitCodesigningSource(
os.makedirs(workdir_path, exist_ok=True)
profile_source = GitProvisioningProfileSource(
working_dir=workdir_path,
repo_url=arguments.gitCodesigningRepository, repo_url=arguments.gitCodesigningRepository,
team_id=build_configuration.team_id, team_id=build_configuration.team_id,
bundle_id=build_configuration.bundle_id, bundle_id=build_configuration.bundle_id,
profile_type=arguments.gitCodesigningType, codesigning_type=arguments.gitCodesigningType,
password=password, password=password,
always_fetch=arguments.gitCodesigningAlwaysFetch always_fetch=arguments.gitCodesigningAlwaysFetch
) )
elif arguments.provisioningProfilesPath is not None: elif arguments.codesigningInformationPath is not None:
profile_source = DirectoryProvisioningProfileSource( profile_source = DirectoryCodesigningSource(
directory_path=arguments.provisioningProfilesPath, directory_path=arguments.codesigningInformationPath,
team_id=build_configuration.team_id, team_id=build_configuration.team_id,
bundle_id=build_configuration.bundle_id bundle_id=build_configuration.bundle_id
) )
else: 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) configuration_repository_path = '{}/build-input/configuration-repository'.format(base_path)
os.makedirs(configuration_repository_path, exist_ok=True) 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) shutil.rmtree(provisioning_path)
os.makedirs(provisioning_path, exist_ok=True) 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 = [] provisioning_profile_files = []
for file_name in os.listdir(provisioning_path): 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'.format(file_name))
file.write('])\n') 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): def generate_project(bazel, arguments):
@ -469,10 +482,13 @@ def generate_project(bazel, arguments):
bazel_command_line.set_continue_on_error(arguments.continueOnError) 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) resolve_configuration(
base_path=os.getcwd(),
if bazel_command_line is not None: bazel_command_line=bazel_command_line,
bazel_command_line.set_configuration_path(configuration_repository_path) arguments=arguments,
aps_environment=arguments.apsEnvironment,
additional_codesigning_output_path=None
)
bazel_command_line.set_build_number(arguments.buildNumber) bazel_command_line.set_build_number(arguments.buildNumber)
@ -516,7 +532,13 @@ def build(bazel, arguments):
elif arguments.cacheHost is not None: elif arguments.cacheHost is not None:
bazel_command_line.add_remote_cache(arguments.cacheHost) 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_configuration(arguments.configuration)
bazel_command_line.set_build_number(arguments.buildNumber) bazel_command_line.set_build_number(arguments.buildNumber)
@ -528,6 +550,36 @@ def build(bazel, arguments):
bazel_command_line.invoke_build() 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): def test(bazel, arguments):
bazel_command_line = BazelCommandLine( bazel_command_line = BazelCommandLine(
@ -542,7 +594,13 @@ def test(bazel, arguments):
elif arguments.cacheHost is not None: elif arguments.cacheHost is not None:
bazel_command_line.add_remote_cache(arguments.cacheHost) 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_configuration('debug_sim_arm64')
bazel_command_line.set_build_number('10000') 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 = 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( codesigning_group.add_argument(
'--gitCodesigningRepository', '--gitCodesigningRepository',
help=''' help='''
@ -578,15 +629,21 @@ def add_codesigning_common_arguments(current_parser: argparse.ArgumentParser):
metavar='path' metavar='path'
) )
codesigning_group.add_argument( codesigning_group.add_argument(
'--provisioningProfilesPath', '--codesigningInformationPath',
help=''' help='''
Use provisioning profiles from a local directory. Use signing certificates and provisioning profiles from a local directory.
''', ''',
metavar='command' metavar='command'
) )
current_parser.add_argument( current_parser.add_argument(
'--gitCodesigningType', '--gitCodesigningType',
choices=[
'development',
'adhoc',
'appstore',
'enterprise'
],
required=False, required=False,
help=''' help='''
The name of the folder to use inside "profiles" folder in the git repository. The name of the folder to use inside "profiles" folder in the git repository.
@ -793,6 +850,12 @@ if __name__ == '__main__':
default=False, default=False,
help='Enable sandbox.', 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.') remote_build_parser = subparsers.add_parser('remote-build', help='Build the app using a remote environment.')
add_codesigning_common_arguments(remote_build_parser) add_codesigning_common_arguments(remote_build_parser)
@ -802,7 +865,7 @@ if __name__ == '__main__':
type=str, type=str,
help='DarwinContainers host address.' help='DarwinContainers host address.'
) )
buildParser.add_argument( remote_build_parser.add_argument(
'--configuration', '--configuration',
choices=[ choices=[
'debug_universal', 'debug_universal',
@ -816,7 +879,7 @@ if __name__ == '__main__':
help='Build configuration' help='Build configuration'
) )
remote_build_parser.add_argument( remote_build_parser.add_argument(
'--bazelCacheHost', '--cacheHost',
required=False, required=False,
type=str, type=str,
help='Bazel remote cache host address.' help='Bazel remote cache host address.'
@ -850,24 +913,27 @@ if __name__ == '__main__':
elif args.commandName == 'remote-build': elif args.commandName == 'remote-build':
base_path = os.getcwd() base_path = os.getcwd()
remote_input_path = '{}/build-input/remote-input'.format(base_path) remote_input_path = '{}/build-input/remote-input'.format(base_path)
certificates_path = '{}/certs'.format(remote_input_path) if os.path.exists(remote_input_path):
provisioning_profiles_path = '{}/profiles'.format(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) resolve_configuration(
os.makedirs(provisioning_profiles_path, exist_ok=True) base_path=os.getcwd(),
bazel_command_line=None,
configuration_repository_path = resolve_configuration(base_path=os.getcwd(), bazel_command_line=None, arguments=arguments, aps_environment='production') arguments=args,
aps_environment='production',
certificates_path = '{}/certs'.format(configuration_repository_path) additional_codesigning_output_path=remote_input_path
provisioning_profiles_path = '{}/profiles'.format(configuration_repository_path) )
shutil.copyfile(args.configurationPath, remote_input_path + '/configuration.json')
RemoteBuild.remote_build( RemoteBuild.remote_build(
darwin_containers_host=args.darwinContainersHost, darwin_containers_host=args.darwinContainersHost,
bazel_cache_host=args.bazelCacheHost, bazel_cache_host=args.cacheHost,
configuration=args.configuration, configuration=args.configuration,
certificates_path=certificates_path, build_input_data_path=remote_input_path
provisioning_profiles_path=provisioning_profiles_path,
configurationPath=args.configurationPath
) )
elif args.commandName == 'test': elif args.commandName == 'test':
test(bazel=bazel_path, arguments=args) test(bazel=bazel_path, arguments=args)

View File

@ -35,7 +35,9 @@ def session_ssh(session, command):
) )
return os.system(ssh_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 from darwin_containers import DarwinContainers
base_dir = os.getcwd() 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) build_number = build_number_offset + int(commit_count)
print('Build number: {}'.format(build_number)) 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) image_name = 'macos-{macos_version}-xcode-{xcode_version}'.format(macos_version=macos_version, xcode_version=xcode_version)
print('Image name: {}'.format(image_name)) 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...') print('Opening container session...')
with darwinContainers.workingImageSession(name=image_name) as session: with darwinContainers.workingImageSession(name=image_name) as session:
print('Uploading data to container...') print('Uploading data to container...')
session_scp_upload(session=session, source_path=certificates_path, destination_path='certs') session_scp_upload(session=session, source_path=build_input_data_path, destination_path='telegram-build-input')
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='{base_dir}/{buildbox_dir}/transient-data/source.tar'.format(base_dir=base_dir, buildbox_dir=buildbox_dir), destination_path='') 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 = ''' guest_build_sh = '''
mkdir telegram-ios set -x
cd telegram-ios set -e
tar -xf ../source.tar
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() guest_build_file_path = tempfile.mktemp()
with open(guest_build_file_path, 'w+') as file: with open(guest_build_file_path, 'w+') as file:
file.write(guest_build_sh) file.write(guest_build_sh)
@ -114,8 +120,6 @@ def remote_build(darwin_containers_host, bazel_cache_host, configuration, certif
print('Executing remote build...') print('Executing remote build...')
if bazel_cache_host is None:
bazel_cache_host = ''
session_ssh(session=session, command='bash -l guest-build-telegram.sh') session_ssh(session=session, command='bash -l guest-build-telegram.sh')
print('Retrieving build artifacts...') print('Retrieving build artifacts...')
@ -125,5 +129,10 @@ def remote_build(darwin_containers_host, bazel_cache_host, configuration, certif
shutil.rmtree(artifacts_path) shutil.rmtree(artifacts_path)
os.makedirs(artifacts_path, exist_ok=True) 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)) session_scp_download(session=session, source_path='/Users/Shared/telegram-ios/build/artifacts/*', destination_path='{artifacts_path}/'.format(artifacts_path=artifacts_path))
print('Artifacts have been stored at {}'.format(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)

View File

@ -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)