##// END OF EJS Templates
rust-hgpath: add `repr(transparent)` to `HgPath`...
rust-hgpath: add `repr(transparent)` to `HgPath` It's been stabilized a long time ago, so let's not rely on an implementation detail now. Differential Revision: https://phab.mercurial-scm.org/D12433

File last commit:

r49702:4561ec90 default
r49868:9dcfd1d0 stable
Show More
cli.py
576 lines | 15.0 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(
Gregory Szorc
automation: support building Python 3 Inno installers...
r45278 hga: HGAutomation,
aws_region,
python_version,
arch,
revision,
version,
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 Python 3 Inno installers...
r45278 for py_version in python_version:
for a in arch:
windows.build_inno_installer(
instance.winrm_client,
py_version,
a,
DIST_PATH,
version=version,
)
Gregory Szorc
automation: perform tasks on remote machines...
r42191
Augie Fackler
formatting: blacken the codebase...
r43346 def build_wix(
Gregory Szorc
automation: support building Python 3 MSI installers...
r45279 hga: HGAutomation,
aws_region,
python_version,
arch,
revision,
version,
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 Python 3 MSI installers...
r45279 for py_version in python_version:
for a in arch:
windows.build_wix_installer(
instance.winrm_client,
py_version,
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...
r45275 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...
r45275 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)
Gregory Szorc
automation: use m6i instances...
r49638 with aws.temporary_windows_dev_instances(c, image, 'm6i.large') as insts:
Gregory Szorc
automation: perform tasks on remote machines...
r42191 instance = insts[0]
winrm_client = instance.winrm_client
windows.synchronize_hg(SOURCE_ROOT, revision, instance)
Gregory Szorc
automation: support Python 3.10 on Windows...
r49180 for py_version in ("2.7", "3.7", "3.8", "3.9", "3.10"):
Gregory Szorc
automation: support building Windows wheels for Python 3.7 and 3.8...
r45275 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: support building Python 3 Inno installers...
r45278 for py_version in (2, 3):
for arch in ('x86', 'x64'):
windows.purge_hg(winrm_client)
windows.build_inno_installer(
winrm_client, py_version, arch, DIST_PATH, version=version
)
Gregory Szorc
automation: support building Python 3 MSI installers...
r45279 windows.build_wix_installer(
winrm_client, py_version, 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
formating: upgrade to black 20.8b1...
r46554 '--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
formating: upgrade to black 20.8b1...
r46554 'bootstrap-linux-dev',
help='Bootstrap Linux development environments',
Gregory Szorc
automation: initial support for running Linux tests...
r42471 )
sp.add_argument(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 '--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
formating: upgrade to black 20.8b1...
r46554 'build-all-windows-packages',
help='Build all Windows packages',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 '--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
formating: upgrade to black 20.8b1...
r46554 '--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
formating: upgrade to black 20.8b1...
r46554 'build-inno',
help='Build Inno Setup installer(s)',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Gregory Szorc
automation: support building Python 3 Inno installers...
r45278 '--python-version',
help='Which version of Python to target',
choices={2, 3},
type=int,
nargs='*',
default=[3],
)
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
formating: upgrade to black 20.8b1...
r46554 '--revision',
help='Mercurial revision to build',
default='.',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 '--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
formating: upgrade to black 20.8b1...
r46554 '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...
r45275 '--python-version',
help='Python version to build for',
Gregory Szorc
automation: support Python 3.10 on Windows...
r49180 choices={'2.7', '3.7', '3.8', '3.9', '3.10'},
Gregory Szorc
automation: support building Windows wheels for Python 3.7 and 3.8...
r45275 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
formating: upgrade to black 20.8b1...
r46554 '--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(
Gregory Szorc
automation: support building Python 3 MSI installers...
r45279 '--python-version',
help='Which version of Python to target',
choices={2, 3},
type=int,
nargs='*',
default=[3],
)
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
formating: upgrade to black 20.8b1...
r46554 '--revision',
help='Mercurial revision to build',
default='.',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 '--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
formating: upgrade to black 20.8b1...
r46554 '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
formating: upgrade to black 20.8b1...
r46554 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
formating: upgrade to black 20.8b1...
r46554 'run-tests-windows',
help='Run tests on Windows',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 '--instance-type',
help='EC2 instance type to use',
Gregory Szorc
automation: use m6i instances...
r49638 default='m6i.large',
Gregory Szorc
automation: perform tasks on remote machines...
r42191 )
sp.add_argument(
'--python-version',
help='Python version to use',
Gregory Szorc
automation: support Python 3.10 on Windows...
r49180 choices={'2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10'},
Gregory Szorc
automation: perform tasks on remote machines...
r42191 default='2.7',
)
sp.add_argument(
'--arch',
help='Architecture to test',
choices={'x86', 'x64'},
default='x64',
)
sp.add_argument(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 '--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
formating: upgrade to black 20.8b1...
r46554 '--ssh-username',
help='SSH username for mercurial-scm.org',
Gregory Szorc
automation: implement "publish-windows-artifacts" command...
r43177 )
sp.add_argument(
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 '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)