##// END OF EJS Templates
automation: support building Windows wheels for Python 3.7 and 3.8...
automation: support building Windows wheels for Python 3.7 and 3.8 The time has come to support Python 3 on Windows. Let's teach our automation code to produce Windows wheels for Python 3.7 and 3.8. We could theoretically support 3.5 and 3.6. But I don't think it is worth it. People on Windows generally use the Mercurial installers, not wheels. And I'd prefer we limit variability and not have to worry about supporting earlier Python versions if it can be helped. As part of this, we change the invocation of pip to `python.exe -m pip`, as this is what is being recommended in Python docs these days. And it seemed to be required to avoid a weird build error. Why, I'm not sure. But it looks like pip was having trouble finding a Visual Studio files when invoked as `pip.exe` but not when using `python.exe -m pip`. Who knows. Differential Revision: https://phab.mercurial-scm.org/D8478

File last commit:

r45261:48096e26 default
r45261:48096e26 default
Show More
cli.py
510 lines | 13.9 KiB | text/x-python | PythonLexer
Gregory Szorc
automation: perform tasks on remote machines...
r42191 # cli.py - Command line interface for automation
#
# Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
# no-check-code because Python 3 native.
import argparse
Gregory Szorc
automation: initial support for running Linux tests...
r42471 import concurrent.futures as futures
Gregory Szorc
automation: perform tasks on remote machines...
r42191 import os
import pathlib
Gregory Szorc
automation: initial support for running Linux tests...
r42471 import time
Gregory Szorc
automation: perform tasks on remote machines...
r42191
from . import (
aws,
HGAutomation,
Gregory Szorc
automation: initial support for running Linux tests...
r42471 linux,
Gregory Szorc
automation: add a command to submit to a Try server...
r43327 try_server,
Gregory Szorc
automation: perform tasks on remote machines...
r42191 windows,
)
Augie Fackler
formatting: blacken the codebase...
r43346 SOURCE_ROOT = pathlib.Path(
os.path.abspath(__file__)
).parent.parent.parent.parent
Gregory Szorc
automation: perform tasks on remote machines...
r42191 DIST_PATH = SOURCE_ROOT / 'dist'
Augie Fackler
formatting: blacken the codebase...
r43346 def bootstrap_linux_dev(
hga: HGAutomation, aws_region, distros=None, parallel=False
):
Gregory Szorc
automation: initial support for running Linux tests...
r42471 c = hga.aws_connection(aws_region)
if distros:
distros = distros.split(',')
else:
distros = sorted(linux.DISTROS)
# TODO There is a wonky interaction involving KeyboardInterrupt whereby
# the context manager that is supposed to terminate the temporary EC2
# instance doesn't run. Until we fix this, make parallel building opt-in
# so we don't orphan instances.
if parallel:
fs = []
with futures.ThreadPoolExecutor(len(distros)) as e:
for distro in distros:
fs.append(e.submit(aws.ensure_linux_dev_ami, c, distro=distro))
for f in fs:
f.result()
else:
for distro in distros:
aws.ensure_linux_dev_ami(c, distro=distro)
Gregory Szorc
automation: make Windows base image name configurable...
r42871 def bootstrap_windows_dev(hga: HGAutomation, aws_region, base_image_name):
Gregory Szorc
automation: perform tasks on remote machines...
r42191 c = hga.aws_connection(aws_region)
Gregory Szorc
automation: make Windows base image name configurable...
r42871 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 print('Windows development AMI available as %s' % image.id)
Augie Fackler
formatting: blacken the codebase...
r43346 def build_inno(
hga: HGAutomation, aws_region, arch, revision, version, base_image_name
):
Gregory Szorc
automation: perform tasks on remote machines...
r42191 c = hga.aws_connection(aws_region)
Gregory Szorc
automation: make Windows base image name configurable...
r42871 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 DIST_PATH.mkdir(exist_ok=True)
with aws.temporary_windows_dev_instances(c, image, 't3.medium') as insts:
instance = insts[0]
windows.synchronize_hg(SOURCE_ROOT, revision, instance)
for a in arch:
Augie Fackler
formatting: blacken the codebase...
r43346 windows.build_inno_installer(
instance.winrm_client, a, DIST_PATH, version=version
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191
Augie Fackler
formatting: blacken the codebase...
r43346 def build_wix(
hga: HGAutomation, aws_region, arch, revision, version, base_image_name
):
Gregory Szorc
automation: perform tasks on remote machines...
r42191 c = hga.aws_connection(aws_region)
Gregory Szorc
automation: make Windows base image name configurable...
r42871 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 DIST_PATH.mkdir(exist_ok=True)
with aws.temporary_windows_dev_instances(c, image, 't3.medium') as insts:
instance = insts[0]
windows.synchronize_hg(SOURCE_ROOT, revision, instance)
for a in arch:
Augie Fackler
formatting: blacken the codebase...
r43346 windows.build_wix_installer(
instance.winrm_client, a, DIST_PATH, version=version
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191
Augie Fackler
formatting: blacken the codebase...
r43346 def build_windows_wheel(
Gregory Szorc
automation: support building Windows wheels for Python 3.7 and 3.8...
r45261 hga: HGAutomation,
aws_region,
python_version,
arch,
revision,
base_image_name,
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Gregory Szorc
automation: perform tasks on remote machines...
r42191 c = hga.aws_connection(aws_region)
Gregory Szorc
automation: make Windows base image name configurable...
r42871 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 DIST_PATH.mkdir(exist_ok=True)
with aws.temporary_windows_dev_instances(c, image, 't3.medium') as insts:
instance = insts[0]
windows.synchronize_hg(SOURCE_ROOT, revision, instance)
Gregory Szorc
automation: support building Windows wheels for Python 3.7 and 3.8...
r45261 for py_version in python_version:
for a in arch:
windows.build_wheel(
instance.winrm_client, py_version, a, DIST_PATH
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191
Augie Fackler
formatting: blacken the codebase...
r43346 def build_all_windows_packages(
hga: HGAutomation, aws_region, revision, version, base_image_name
):
Gregory Szorc
automation: perform tasks on remote machines...
r42191 c = hga.aws_connection(aws_region)
Gregory Szorc
automation: make Windows base image name configurable...
r42871 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 DIST_PATH.mkdir(exist_ok=True)
with aws.temporary_windows_dev_instances(c, image, 't3.medium') as insts:
instance = insts[0]
winrm_client = instance.winrm_client
windows.synchronize_hg(SOURCE_ROOT, revision, instance)
Gregory Szorc
automation: support building Windows wheels for Python 3.7 and 3.8...
r45261 for py_version in ("2.7", "3.7", "3.8"):
for arch in ("x86", "x64"):
windows.purge_hg(winrm_client)
windows.build_wheel(
winrm_client,
python_version=py_version,
arch=arch,
dest_path=DIST_PATH,
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 for arch in ('x86', 'x64'):
windows.purge_hg(winrm_client)
Augie Fackler
formatting: blacken the codebase...
r43346 windows.build_inno_installer(
winrm_client, arch, DIST_PATH, version=version
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 windows.purge_hg(winrm_client)
Augie Fackler
formatting: blacken the codebase...
r43346 windows.build_wix_installer(
winrm_client, arch, DIST_PATH, version=version
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191
def terminate_ec2_instances(hga: HGAutomation, aws_region):
Gregory Szorc
automation: don't create resources when deleting things...
r42463 c = hga.aws_connection(aws_region, ensure_ec2_state=False)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 aws.terminate_ec2_instances(c.ec2resource)
def purge_ec2_resources(hga: HGAutomation, aws_region):
Gregory Szorc
automation: don't create resources when deleting things...
r42463 c = hga.aws_connection(aws_region, ensure_ec2_state=False)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 aws.remove_resources(c)
Augie Fackler
formatting: blacken the codebase...
r43346 def run_tests_linux(
hga: HGAutomation,
aws_region,
instance_type,
python_version,
test_flags,
distro,
filesystem,
):
Gregory Szorc
automation: initial support for running Linux tests...
r42471 c = hga.aws_connection(aws_region)
image = aws.ensure_linux_dev_ami(c, distro=distro)
t_start = time.time()
ensure_extra_volume = filesystem not in ('default', 'tmpfs')
with aws.temporary_linux_dev_instances(
Augie Fackler
formatting: blacken the codebase...
r43346 c, image, instance_type, ensure_extra_volume=ensure_extra_volume
) as insts:
Gregory Szorc
automation: initial support for running Linux tests...
r42471
instance = insts[0]
Augie Fackler
formatting: blacken the codebase...
r43346 linux.prepare_exec_environment(
instance.ssh_client, filesystem=filesystem
)
Gregory Szorc
automation: initial support for running Linux tests...
r42471 linux.synchronize_hg(SOURCE_ROOT, instance, '.')
t_prepared = time.time()
Augie Fackler
formatting: blacken the codebase...
r43346 linux.run_tests(instance.ssh_client, python_version, test_flags)
Gregory Szorc
automation: initial support for running Linux tests...
r42471 t_done = time.time()
t_setup = t_prepared - t_start
t_all = t_done - t_start
print(
'total time: %.1fs; setup: %.1fs; tests: %.1fs; setup overhead: %.1f%%'
Augie Fackler
formatting: blacken the codebase...
r43346 % (t_all, t_setup, t_done - t_prepared, t_setup / t_all * 100.0)
)
Gregory Szorc
automation: initial support for running Linux tests...
r42471
Augie Fackler
formatting: blacken the codebase...
r43346 def run_tests_windows(
hga: HGAutomation,
aws_region,
instance_type,
python_version,
arch,
test_flags,
base_image_name,
):
Gregory Szorc
automation: perform tasks on remote machines...
r42191 c = hga.aws_connection(aws_region)
Gregory Szorc
automation: make Windows base image name configurable...
r42871 image = aws.ensure_windows_dev_ami(c, base_image_name=base_image_name)
Gregory Szorc
automation: perform tasks on remote machines...
r42191
Augie Fackler
formatting: blacken the codebase...
r43346 with aws.temporary_windows_dev_instances(
c, image, instance_type, disable_antivirus=True
) as insts:
Gregory Szorc
automation: perform tasks on remote machines...
r42191 instance = insts[0]
windows.synchronize_hg(SOURCE_ROOT, '.', instance)
Augie Fackler
formatting: blacken the codebase...
r43346 windows.run_tests(
instance.winrm_client, python_version, arch, test_flags
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191
Augie Fackler
formatting: blacken the codebase...
r43346 def publish_windows_artifacts(
hg: HGAutomation,
aws_region,
version: str,
pypi: bool,
mercurial_scm_org: bool,
ssh_username: str,
):
windows.publish_artifacts(
DIST_PATH,
version,
pypi=pypi,
mercurial_scm_org=mercurial_scm_org,
ssh_username=ssh_username,
)
Gregory Szorc
automation: implement "publish-windows-artifacts" command...
r43177
Gregory Szorc
automation: add a command to submit to a Try server...
r43327 def run_try(hga: HGAutomation, aws_region: str, rev: str):
c = hga.aws_connection(aws_region, ensure_ec2_state=False)
try_server.trigger_try(c, rev=rev)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 def get_parser():
parser = argparse.ArgumentParser()
parser.add_argument(
'--state-path',
default='~/.hgautomation',
help='Path for local state files',
)
parser.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--aws-region', help='AWS region to use', default='us-west-2',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
subparsers = parser.add_subparsers()
sp = subparsers.add_parser(
Augie Fackler
formatting: blacken the codebase...
r43346 'bootstrap-linux-dev', help='Bootstrap Linux development environments',
Gregory Szorc
automation: initial support for running Linux tests...
r42471 )
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--distros', help='Comma delimited list of distros to bootstrap',
Gregory Szorc
automation: initial support for running Linux tests...
r42471 )
sp.add_argument(
'--parallel',
action='store_true',
Augie Fackler
formatting: blacken the codebase...
r43346 help='Generate AMIs in parallel (not CTRL-c safe)',
Gregory Szorc
automation: initial support for running Linux tests...
r42471 )
sp.set_defaults(func=bootstrap_linux_dev)
sp = subparsers.add_parser(
Gregory Szorc
automation: perform tasks on remote machines...
r42191 'bootstrap-windows-dev',
help='Bootstrap the Windows development environment',
)
Gregory Szorc
automation: make Windows base image name configurable...
r42871 sp.add_argument(
'--base-image-name',
help='AMI name of base image',
default=aws.WINDOWS_BASE_IMAGE_NAME,
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 sp.set_defaults(func=bootstrap_windows_dev)
sp = subparsers.add_parser(
Augie Fackler
formatting: blacken the codebase...
r43346 'build-all-windows-packages', help='Build all Windows packages',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--revision', help='Mercurial revision to build', default='.',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
Gregory Szorc
automation: add --version argument to build-all-windows-packages...
r42469 sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--version', help='Mercurial version string to use',
Gregory Szorc
automation: add --version argument to build-all-windows-packages...
r42469 )
Gregory Szorc
automation: make Windows base image name configurable...
r42871 sp.add_argument(
'--base-image-name',
help='AMI name of base image',
default=aws.WINDOWS_BASE_IMAGE_NAME,
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 sp.set_defaults(func=build_all_windows_packages)
sp = subparsers.add_parser(
Augie Fackler
formatting: blacken the codebase...
r43346 'build-inno', help='Build Inno Setup installer(s)',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
'--arch',
help='Architecture to build for',
choices={'x86', 'x64'},
nargs='*',
default=['x64'],
)
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--revision', help='Mercurial revision to build', default='.',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--version', help='Mercurial version string to use in installer',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
Gregory Szorc
automation: make Windows base image name configurable...
r42871 sp.add_argument(
'--base-image-name',
help='AMI name of base image',
default=aws.WINDOWS_BASE_IMAGE_NAME,
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 sp.set_defaults(func=build_inno)
sp = subparsers.add_parser(
Augie Fackler
formatting: blacken the codebase...
r43346 'build-windows-wheel', help='Build Windows wheel(s)',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Gregory Szorc
automation: support building Windows wheels for Python 3.7 and 3.8...
r45261 '--python-version',
help='Python version to build for',
choices={'2.7', '3.7', '3.8'},
nargs='*',
default=['3.8'],
)
sp.add_argument(
Gregory Szorc
automation: perform tasks on remote machines...
r42191 '--arch',
help='Architecture to build for',
choices={'x86', 'x64'},
nargs='*',
default=['x64'],
)
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--revision', help='Mercurial revision to build', default='.',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
Gregory Szorc
automation: make Windows base image name configurable...
r42871 sp.add_argument(
'--base-image-name',
help='AMI name of base image',
default=aws.WINDOWS_BASE_IMAGE_NAME,
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 sp.set_defaults(func=build_windows_wheel)
Augie Fackler
formatting: blacken the codebase...
r43346 sp = subparsers.add_parser('build-wix', help='Build WiX installer(s)')
Gregory Szorc
automation: perform tasks on remote machines...
r42191 sp.add_argument(
'--arch',
help='Architecture to build for',
choices={'x86', 'x64'},
nargs='*',
default=['x64'],
)
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--revision', help='Mercurial revision to build', default='.',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--version', help='Mercurial version string to use in installer',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
Gregory Szorc
automation: make Windows base image name configurable...
r42871 sp.add_argument(
'--base-image-name',
help='AMI name of base image',
default=aws.WINDOWS_BASE_IMAGE_NAME,
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 sp.set_defaults(func=build_wix)
sp = subparsers.add_parser(
'terminate-ec2-instances',
help='Terminate all active EC2 instances managed by us',
)
sp.set_defaults(func=terminate_ec2_instances)
sp = subparsers.add_parser(
Augie Fackler
formatting: blacken the codebase...
r43346 'purge-ec2-resources', help='Purge all EC2 resources managed by us',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.set_defaults(func=purge_ec2_resources)
Augie Fackler
formatting: blacken the codebase...
r43346 sp = subparsers.add_parser('run-tests-linux', help='Run tests on Linux',)
Gregory Szorc
automation: initial support for running Linux tests...
r42471 sp.add_argument(
'--distro',
help='Linux distribution to run tests on',
choices=linux.DISTROS,
Gregory Szorc
automation: support and use Debian Buster by default...
r43288 default='debian10',
Gregory Szorc
automation: initial support for running Linux tests...
r42471 )
sp.add_argument(
'--filesystem',
help='Filesystem type to use',
choices={'btrfs', 'default', 'ext3', 'ext4', 'jfs', 'tmpfs', 'xfs'},
default='default',
)
sp.add_argument(
'--instance-type',
help='EC2 instance type to use',
default='c5.9xlarge',
)
sp.add_argument(
'--python-version',
help='Python version to use',
Augie Fackler
formatting: blacken the codebase...
r43346 choices={
'system2',
'system3',
'2.7',
'3.5',
'3.6',
'3.7',
'3.8',
'pypy',
'pypy3.5',
'pypy3.6',
},
Gregory Szorc
automation: initial support for running Linux tests...
r42471 default='system2',
)
sp.add_argument(
'test_flags',
help='Extra command line flags to pass to run-tests.py',
nargs='*',
)
sp.set_defaults(func=run_tests_linux)
sp = subparsers.add_parser(
Augie Fackler
formatting: blacken the codebase...
r43346 'run-tests-windows', help='Run tests on Windows',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--instance-type', help='EC2 instance type to use', default='t3.medium',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
'--python-version',
help='Python version to use',
choices={'2.7', '3.5', '3.6', '3.7', '3.8'},
default='2.7',
)
sp.add_argument(
'--arch',
help='Architecture to test',
choices={'x86', 'x64'},
default='x64',
)
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--test-flags', help='Extra command line flags to pass to run-tests.py',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
Gregory Szorc
automation: make Windows base image name configurable...
r42871 sp.add_argument(
'--base-image-name',
help='AMI name of base image',
default=aws.WINDOWS_BASE_IMAGE_NAME,
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 sp.set_defaults(func=run_tests_windows)
Gregory Szorc
automation: implement "publish-windows-artifacts" command...
r43177 sp = subparsers.add_parser(
'publish-windows-artifacts',
Augie Fackler
formatting: blacken the codebase...
r43346 help='Publish built Windows artifacts (wheels, installers, etc)',
Gregory Szorc
automation: implement "publish-windows-artifacts" command...
r43177 )
sp.add_argument(
'--no-pypi',
dest='pypi',
action='store_false',
default=True,
help='Skip uploading to PyPI',
)
sp.add_argument(
'--no-mercurial-scm-org',
dest='mercurial_scm_org',
action='store_false',
default=True,
help='Skip uploading to www.mercurial-scm.org',
)
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 '--ssh-username', help='SSH username for mercurial-scm.org',
Gregory Szorc
automation: implement "publish-windows-artifacts" command...
r43177 )
sp.add_argument(
Augie Fackler
formatting: blacken the codebase...
r43346 'version', help='Mercurial version string to locate local packages',
Gregory Szorc
automation: implement "publish-windows-artifacts" command...
r43177 )
sp.set_defaults(func=publish_windows_artifacts)
Gregory Szorc
automation: add a command to submit to a Try server...
r43327 sp = subparsers.add_parser(
Augie Fackler
formatting: blacken the codebase...
r43346 'try', help='Run CI automation against a custom changeset'
Gregory Szorc
automation: add a command to submit to a Try server...
r43327 )
Augie Fackler
formatting: blacken the codebase...
r43346 sp.add_argument('-r', '--rev', default='.', help='Revision to run CI on')
Gregory Szorc
automation: add a command to submit to a Try server...
r43327 sp.set_defaults(func=run_try)
Gregory Szorc
automation: perform tasks on remote machines...
r42191 return parser
def main():
parser = get_parser()
args = parser.parse_args()
local_state_path = pathlib.Path(os.path.expanduser(args.state_path))
automation = HGAutomation(local_state_path)
if not hasattr(args, 'func'):
parser.print_help()
return
kwargs = dict(vars(args))
del kwargs['func']
del kwargs['state_path']
args.func(automation, **kwargs)