diff options
| author | zousar <[email protected]> | 2022-02-01 21:33:57 -0700 |
|---|---|---|
| committer | zousar <[email protected]> | 2022-02-01 21:33:57 -0700 |
| commit | 7f12ac47b01639e8e67d04388a6f971271d25c52 (patch) | |
| tree | 53ac2f2a844785bf5543cd62a692985f0b0ff385 /scripts | |
| parent | Merge branch 'main' into non-elevated-asio (diff) | |
| parent | CacheRecordPolicy: Fix inverted PolicyMask expression that caused parsing Val... (diff) | |
| download | zen-7f12ac47b01639e8e67d04388a6f971271d25c52.tar.xz zen-7f12ac47b01639e8e67d04388a6f971271d25c52.zip | |
Merge branch 'main' into non-elevated-asio
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/remote_build.py | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/scripts/remote_build.py b/scripts/remote_build.py new file mode 100644 index 000000000..c5787f635 --- /dev/null +++ b/scripts/remote_build.py @@ -0,0 +1,270 @@ +import os +import sys +import argparse +import subprocess +from pathlib import Path + +# {{{1 misc -------------------------------------------------------------------- + +# Disables output of ANSI codes if the terminal doesn't support them +if os.name == "nt": + from ctypes import windll, c_int, byref + stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) + mode = c_int(0) + windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode)) + ansi_on = (mode.value & 4) != 0 +else: + ansi_on = True + +#------------------------------------------------------------------------------- +def _header(*args, ansi=96): + if ansi_on: + print(f"\x1b[{ansi}m##", *args, end="") + print("\x1b[0m") + else: + print("\n##", *args) + +#------------------------------------------------------------------------------- +def _run_checked(cmd, *args, **kwargs): + _header(cmd, *args, ansi=97) + ret = subprocess.run((cmd, *args), **kwargs, bufsize=0) + if ret.returncode: + raise RuntimeError("Failed running " + str(cmd)) + +#------------------------------------------------------------------------------- +def _get_ip(): + import socket + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect(("172.31.255.255", 1)) + return s.getsockname()[0] + except: + return "127.0.0.1" + finally: + s.close() + +#------------------------------------------------------------------------------- +def _find_binary(name): + name += ".exe" if os.name == "nt" else "" + for prefix in os.getenv("PATH", "").split(os.pathsep): + path = Path(prefix) / name + if path.is_file(): + return path + raise EnvironmentError(f"Unable to find '{name}' in the path") + +#------------------------------------------------------------------------------- +class _AutoKill(object): + def __init__(self, proc): + self._proc = proc + + def __del__(self): + self._proc.kill() + self._proc.wait() + pass + + + +# {{{1 local ------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +def _local(args): + # Parse arguments + desc = "Build Zen on a remote host" + parser = argparse.ArgumentParser(description=desc) + parser.add_argument("remotehost", help="") + parser.add_argument("action", default="build", nargs="?", help="") + parser.add_argument("--keyfile", default=None, help="SSH key file") + args = parser.parse_args(args) + + # Find the binaries we'll need + _header("Finding tools") + git_bin = _find_binary("git") + print(f"Using git from '{git_bin.name}' from '{git_bin.parent}'") + + def find_git_tool(git_bin, tool_name): + print(f"Locating {tool_name}...") + tool_suffix = "usr/bin/" + tool_name + tool_suffix += ".exe" if os.name == "nt" else "" + for parent in git_bin.parents: + tool_path = parent / tool_suffix + if tool_path.is_file(): + return tool_path + return _find_binary(tool_name) + + ssh_bin = find_git_tool(git_bin, "ssh") + scp_bin = find_git_tool(git_bin, "scp") + print(f"Using '{ssh_bin.name}' from '{ssh_bin.parent}'") + print(f"Using '{scp_bin.name}' from '{scp_bin.parent}'") + + # Find the Zen repository root + for parent in Path(__file__).resolve().parents: + if (parent / ".git").is_dir(): + zen_dir = parent + break; + else: + raise EnvironmentError("Unable to find '.git/' directory") + + _header("Validating remote host and credentials") + + # Validate key file. OpenSSL needs a trailing EOL, LibreSSL doesn't + if args.keyfile: + with open(args.keyfile, "rt") as key_file: + lines = [x for x in key_file] + if not lines[-1].endswith("\n"): + print("!! ERROR: key file must end with a new line") + return 1 + identity = ("-i", args.keyfile) + else: + identity = () + + # Validate remote host + host = args.remotehost + if host == "linux": host = os.getenv("ZEN_REMOTE_HOST_LINUX", "arn-lin-12345") + if host == "mac": host = os.getenv("ZEN_REMOTE_HOST_MAC", "imacpro-arn.local") + """ + keygen_bin = find_git_tool(git_bin, "ssh-keygen") + print(f"Using '{keygen_bin.name}' from '{keygen_bin.parent}'") + known_host = subprocess.run((keygen_bin, "-F", host)).returncode + if not known_host: + print("Adding", host, "as a known host") + print("ANSWER 'yes'") + known_host = subprocess.run((ssh_bin, *identity, "zenbuild@" + host, "uname -a")).returncode + raise IndexError + """ + host = "zenbuild@" + host + print(f"Using host '{host}'") + + # Start a git daemon to use as a transfer mechanism + _header("Starting a git daemon") + print("Port: 4493") + print("Base-path: ", zen_dir) + print("Host: ", _get_ip()) + daemon = subprocess.Popen( + ( git_bin, + "daemon", + "--port=4493", + "--export-all", + "--reuseaddr", + "--verbose", + "--informative-errors", + "--base-path=" + str(zen_dir) ), + #stdout = daemon_log, + stderr = subprocess.STDOUT + ) + daemon_killer = _AutoKill(daemon) + + # Run this script on the remote machine + _header("Running SSH") + + remote_zen_dir = "%s_%s" % (os.getlogin(), _get_ip()) + print(f"Using zen '~/{remote_zen_dir}'") + + print(f"Running {__file__} remotely") + with open(__file__, "rt") as self_file: + _run_checked( + ssh_bin, + *identity, + "-tA", + host, + f"python3 -u - !remote {_get_ip()} '{remote_zen_dir}' main '{args.action}'", + stdin=self_file) + + # If we're bundling, collect zip files from the remote machine + if args.action == "bundle": + build_dir = zen_dir / "build" + build_dir.mkdir(exist_ok=True) + scp_args = (*identity, host + f":zen/{remote_zen_dir}/build/*.zip", build_dir) + _run_checked("scp", *scp_args) + + + +# {{{1 remote ------------------------------------------------------------------ + +#------------------------------------------------------------------------------- +def _remote(args): + # Parse arguments + desc = "Build Zen on a remote host" + parser = argparse.ArgumentParser(description=desc) + parser.add_argument("ip", help="Host's IP address") + parser.add_argument("reponame", help="Repository name clone into and work in") + parser.add_argument("branch", help="Zen branch to operate on") + parser.add_argument("action", help="The action to do") + args = parser.parse_args(args) + + # Homeward bound and out + zen_dir = Path().home() / "zen" + os.chdir(zen_dir) + + # Mutual exclusion + """ + lock_path = zen_dir / "../.remote_lock" + try: lock_file = open(lock_path, "xb") + except: raise RuntimeError("Failed to lock", lock_path) + """ + + # Check for a clone, create it, chdir to it + _header("REMOTE:", f"Clone/pull from {args.ip}") + clone_dir = zen_dir / args.reponame + if not clone_dir.is_dir(): + _run_checked("git", "clone", f"git://{args.ip}:4493/", clone_dir) + os.chdir(clone_dir) + + _run_checked("git", "checkout", args.branch) + _run_checked("git", "pull", "-r") + + _header("REMOTE:", f"Performing action '{args.action}'") + + # Find xmake + xmake_bin = max(x for x in (zen_dir / "xmake").glob("*")) + xmake_bin /= "usr/local/bin/xmake" + + # Run xmake + xmake_env = {} + xmake_env["VCPKG_ROOT"] = zen_dir / "vcpkg" + if sys.platform == "linux": + xmake_env["CXX"] = "g++-11" + print("xmake environment:") + for key, value in xmake_env.items(): + print(" ", key, "=", value) + xmake_env.update(os.environ) + + def run_xmake(*args): + print("starting xmake...", end="\r") + _run_checked(xmake_bin, args[0], "--yes", *args[1:], env=xmake_env) + + if args.action.startswith("build"): + mode = "debug" if args.action == "build.debug" else "release" + run_xmake("config", "--mode=" + mode) + run_xmake("build") + + elif args.action == "bundle": + run_xmake("bundle") + + elif args.action == "test": + run_xmake("config", "--mode=debug") + run_xmake("test") + + elif args.action == "clean": + _run_checked("git", "reset") + _run_checked("git", "checkout", ".") + _run_checked("git", "clean", "-xdf") + + + +# {{{1 entry ------------------------------------------------------------------- +if __name__ == "__main__": + if "!remote" in sys.argv[1:2]: + ret = _remote(sys.argv[2:]) + raise SystemExit(ret) + + try: + ret = _local(sys.argv[1:]) + raise SystemExit(ret) + except: + raise + finally: + # Roundabout way to avoid orphaned git-daemon processes + if os.name == "nt": + os.system("taskkill /f /im git-daemon.exe") + +# vim: expandtab foldlevel=1 foldmethod=marker |