diff --git a/contrib/automation/README.rst b/contrib/automation/README.rst --- a/contrib/automation/README.rst +++ b/contrib/automation/README.rst @@ -33,6 +33,46 @@ into a remote machine, we create a temporary directory for the SSH config so the user's known hosts file isn't updated. +Try Server +========== + +There exists a *Try Server* which allows automation to run against +an arbitrary Mercurial changeset and displays results via the web. + +.. note:: + + The *Try Server* is still experimental infrastructure. + +To use the *Try Server*:: + + $ ./automation.py try + +With a custom AWS profile:: + + $ AWS_PROFILE=hg contrib/automation/automation.py try + +By default, the ``.`` revision is submitted. **Any uncommitted changes +are not submitted.** + +To switch which revision is used:: + + $ ./automation.py try -r abcdef + +Access to the *Try Server* requires access to a special AWS account. +This account is currently run by Gregory Szorc. Here is the procedure +for accessing the *Try Server*: + +1. Email Gregory Szorc at gregory.szorc@gmail.com and request a + username. This username will be stored in the public domain. +2. Wait for an email reply containing your temporary AWS credentials. +3. Log in at https://gregoryszorc-hg.signin.aws.amazon.com/console + and set a new, secure password. +4. Go to https://console.aws.amazon.com/iam/home?region=us-west-2#/security_credentials +5. Under ``Access keys for CLI, SDK, & API access``, click the + ``Create access key`` button. +6. See the ``AWS Integration`` section for instructions on + configuring your local client to use the generated credentials. + AWS Integration =============== diff --git a/contrib/automation/hgautomation/cli.py b/contrib/automation/hgautomation/cli.py --- a/contrib/automation/hgautomation/cli.py +++ b/contrib/automation/hgautomation/cli.py @@ -17,6 +17,7 @@ aws, HGAutomation, linux, + try_server, windows, ) @@ -193,6 +194,11 @@ ssh_username=ssh_username) +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) + + def get_parser(): parser = argparse.ArgumentParser() @@ -439,6 +445,15 @@ ) sp.set_defaults(func=publish_windows_artifacts) + sp = subparsers.add_parser( + 'try', + help='Run CI automation against a custom changeset' + ) + sp.add_argument('-r', '--rev', + default='.', + help='Revision to run CI on') + sp.set_defaults(func=run_try) + return parser diff --git a/contrib/automation/hgautomation/try_server.py b/contrib/automation/hgautomation/try_server.py new file mode 100644 --- /dev/null +++ b/contrib/automation/hgautomation/try_server.py @@ -0,0 +1,99 @@ +# try_server.py - Interact with Try server +# +# Copyright 2019 Gregory Szorc +# +# 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 base64 +import json +import os +import subprocess +import tempfile + +from .aws import AWSConnection + +LAMBDA_FUNCTION = "ci-try-server-upload" + + +def trigger_try(c: AWSConnection, rev="."): + """Trigger a new Try run.""" + lambda_client = c.session.client("lambda") + + cset, bundle = generate_bundle(rev=rev) + + payload = { + "bundle": base64.b64encode(bundle).decode("utf-8"), + "node": cset["node"], + "branch": cset["branch"], + "user": cset["user"], + "message": cset["desc"], + } + + print("resolved revision:") + print("node: %s" % cset["node"]) + print("branch: %s" % cset["branch"]) + print("user: %s" % cset["user"]) + print("desc: %s" % cset["desc"].splitlines()[0]) + print() + + print("sending to Try...") + res = lambda_client.invoke( + FunctionName=LAMBDA_FUNCTION, + InvocationType="RequestResponse", + Payload=json.dumps(payload).encode("utf-8"), + ) + + body = json.load(res["Payload"]) + for message in body: + print("remote: %s" % message) + + +def generate_bundle(rev="."): + """Generate a bundle suitable for use by the Try service. + + Returns a tuple of revision metadata and raw Mercurial bundle data. + """ + # `hg bundle` doesn't support streaming to stdout. So we use a temporary + # file. + path = None + try: + fd, path = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") + os.close(fd) + + args = [ + "hg", + "bundle", + "--type", + "gzip-v2", + "--base", + "public()", + "--rev", + rev, + path, + ] + + print("generating bundle...") + subprocess.run(args, check=True) + + with open(path, "rb") as fh: + bundle_data = fh.read() + + finally: + if path: + os.unlink(path) + + args = [ + "hg", + "log", + "-r", + rev, + # We have to upload as JSON, so it won't matter if we emit binary + # since we need to normalize to UTF-8. + "-T", + "json", + ] + res = subprocess.run(args, check=True, capture_output=True) + return json.loads(res.stdout)[0], bundle_data diff --git a/tests/test-check-code.t b/tests/test-check-code.t --- a/tests/test-check-code.t +++ b/tests/test-check-code.t @@ -18,6 +18,7 @@ Skipping contrib/automation/hgautomation/linux.py it has no-che?k-code (glob) Skipping contrib/automation/hgautomation/pypi.py it has no-che?k-code (glob) Skipping contrib/automation/hgautomation/ssh.py it has no-che?k-code (glob) + Skipping contrib/automation/hgautomation/try_server.py it has no-che?k-code (glob) Skipping contrib/automation/hgautomation/windows.py it has no-che?k-code (glob) Skipping contrib/automation/hgautomation/winrm.py it has no-che?k-code (glob) Skipping contrib/packaging/hgpackaging/downloads.py it has no-che?k-code (glob)