|
| 1 | +# ------------------------------------------------------------------------- |
| 2 | +# Copyright (c) Microsoft Corporation. All rights reserved. |
| 3 | +# Licensed under the MIT License. See License.txt in the project root for |
| 4 | +# license information. |
| 5 | +# -------------------------------------------------------------------------- |
| 6 | + |
| 7 | +import argparse |
| 8 | +import os |
| 9 | +import shlex |
| 10 | +import sys |
| 11 | + |
| 12 | +try: |
| 13 | + from dotenv import load_dotenv |
| 14 | + |
| 15 | + load_dotenv() |
| 16 | +except: |
| 17 | + pass |
| 18 | + |
| 19 | +import subprocess |
| 20 | + |
| 21 | + |
| 22 | +# This file contains a script for managing test recordings in the azure-sdk-assets repository with Docker. |
| 23 | +# |
| 24 | +# INSTRUCTIONS FOR USE: |
| 25 | +# |
| 26 | +# - Set GIT_TOKEN, GIT_COMMIT_OWNER, and GIT_COMMIT_EMAIL environment variables to authenticate git requests. |
| 27 | +# These can be set in-process or added to a .env file at the root of or directory above your local copy of the |
| 28 | +# azure-sdk-for-python repository. |
| 29 | +# - Set your working directory to be inside your local copy of the azure-sdk-for-python repository. |
| 30 | +# - Run the following command: |
| 31 | +# |
| 32 | +# `python {path to script}/manage_recordings.py {verb} {relative path to package's assets.json file}` |
| 33 | +# |
| 34 | +# For example, with the root of the azure-sdk-for-python repo as the working directory, you can push modified |
| 35 | +# azure-keyvault-keys recordings to the assets repo with: |
| 36 | +# |
| 37 | +# `python scripts/manage_recordings.py push sdk/keyvault/azure-keyvault-keys/assets.json` |
| 38 | +# |
| 39 | +# - In addition to "push", you can also use the "restore" or "reset" verbs in the same command format. |
| 40 | +# |
| 41 | +# * push: pushes recording updates to a new assets repo tag and updates the tag pointer in `assets.json`. |
| 42 | +# * restore: fetches recordings from the assets repo, based on the tag pointer in `assets.json`. |
| 43 | +# * reset: discards any pending changes to recordings, based on the tag pointer in `assets.json`. |
| 44 | +# |
| 45 | +# For more information about how recording asset synchronization, please refer to |
| 46 | +# https://github.com/Azure/azure-sdk-tools/blob/main/tools/test-proxy/documentation/asset-sync/README.md. |
| 47 | + |
| 48 | +# Load environment variables from user's .env file |
| 49 | + |
| 50 | +CONTAINER_NAME = "azsdkengsys.azurecr.io/engsys/test-proxy" |
| 51 | +GIT_TOKEN = os.getenv("GIT_TOKEN", "") |
| 52 | +GIT_OWNER = os.getenv("GIT_COMMIT_OWNER", "") |
| 53 | +GIT_EMAIL = os.getenv("GIT_COMMIT_EMAIL", "") |
| 54 | + |
| 55 | + |
| 56 | +# ----- HELPERS ----- # |
| 57 | + |
| 58 | + |
| 59 | +discovered_roots = [] |
| 60 | + |
| 61 | + |
| 62 | +def ascend_to_root(start_dir_or_file: str) -> str: |
| 63 | + """ |
| 64 | + Given a path, ascend until encountering a folder with a `.git` folder present within it. Return that directory. |
| 65 | +
|
| 66 | + :param str start_dir_or_file: The starting directory or file. Either is acceptable. |
| 67 | + """ |
| 68 | + if os.path.isfile(start_dir_or_file): |
| 69 | + current_dir = os.path.dirname(start_dir_or_file) |
| 70 | + else: |
| 71 | + current_dir = start_dir_or_file |
| 72 | + |
| 73 | + while current_dir is not None and not (os.path.dirname(current_dir) == current_dir): |
| 74 | + possible_root = os.path.join(current_dir, ".git") |
| 75 | + |
| 76 | + # we need the git check to prevent ascending out of the repo |
| 77 | + if os.path.exists(possible_root): |
| 78 | + if current_dir not in discovered_roots: |
| 79 | + discovered_roots.append(current_dir) |
| 80 | + return current_dir |
| 81 | + else: |
| 82 | + current_dir = os.path.dirname(current_dir) |
| 83 | + |
| 84 | + raise Exception(f'Requested target "{start_dir_or_file}" does not exist within a git repo.') |
| 85 | + |
| 86 | + |
| 87 | +def delete_container() -> None: |
| 88 | + """Delete container if it remained""" |
| 89 | + proc = subprocess.Popen( |
| 90 | + shlex.split(f"docker rm -f {CONTAINER_NAME}"), |
| 91 | + stdout=subprocess.PIPE, |
| 92 | + stderr=subprocess.PIPE, |
| 93 | + stdin=subprocess.DEVNULL, |
| 94 | + ) |
| 95 | + output, stderr = proc.communicate(timeout=10) |
| 96 | + return None |
| 97 | + |
| 98 | + |
| 99 | +def get_image_tag(repo_root: str) -> str: |
| 100 | + """Gets the test proxy Docker image tag from the target_version.txt file in /eng/common/testproxy""" |
| 101 | + version_file_location = os.path.relpath("eng/common/testproxy/target_version.txt") |
| 102 | + version_file_location_from_root = os.path.abspath(os.path.join(repo_root, version_file_location)) |
| 103 | + |
| 104 | + with open(version_file_location_from_root, "r") as f: |
| 105 | + image_tag = f.read().strip() |
| 106 | + |
| 107 | + return image_tag |
| 108 | + |
| 109 | + |
| 110 | +# ----- CORE LOGIC ----- # |
| 111 | + |
| 112 | + |
| 113 | +if not (GIT_TOKEN and GIT_OWNER and GIT_EMAIL): |
| 114 | + raise ValueError( |
| 115 | + "GIT_TOKEN, GIT_COMMIT_OWNER, and GIT_COMMIT_EMAIL environment variables must be set, " |
| 116 | + "either in-process or in a .env file" |
| 117 | + ) |
| 118 | + |
| 119 | +# Prepare command arguments |
| 120 | +parser = argparse.ArgumentParser(description="Script for managing recording assets with Docker.") |
| 121 | +parser.add_argument("verb", help='The action verb for managing recordings: "restore", "push", or "reset".') |
| 122 | +parser.add_argument( |
| 123 | + "path", |
| 124 | + default="assets.json", |
| 125 | + help='The *relative* path to your package\'s `assets.json` file. Default is "assets.json".', |
| 126 | +) |
| 127 | +args = parser.parse_args() |
| 128 | + |
| 129 | +if args.verb and args.path: |
| 130 | + |
| 131 | + current_directory = os.getcwd() |
| 132 | + repo_root = ascend_to_root(current_directory) |
| 133 | + image_tag = get_image_tag(repo_root) |
| 134 | + |
| 135 | + root_path = os.path.abspath(repo_root) |
| 136 | + cwd_relpath = os.path.relpath(current_directory, root_path) |
| 137 | + assets_path = os.path.join(cwd_relpath, args.path).replace("\\", "/") |
| 138 | + |
| 139 | + delete_container() # Delete any lingering container so a new one can be created with necessary environment variables |
| 140 | + |
| 141 | + subprocess.run( |
| 142 | + shlex.split( |
| 143 | + f'docker run --rm -v "{repo_root}:/srv/testproxy" ' |
| 144 | + f'-e "GIT_TOKEN={GIT_TOKEN}" -e "GIT_COMMIT_OWNER={GIT_OWNER}" -e "GIT_COMMIT_EMAIL={GIT_OWNER}" ' |
| 145 | + f"{CONTAINER_NAME}:{image_tag} test-proxy {args.verb.lower()} -a {assets_path}" |
| 146 | + ), |
| 147 | + stdout=sys.stdout, |
| 148 | + stderr=sys.stderr, |
| 149 | + ) |
0 commit comments