eng/sdk/sync_sdk.py (139 lines of code) (raw):

#!/usr/bin/env python3 # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import os import sys import logging import argparse import subprocess import glob import shutil import json from typing import List sdk_root: str skip_artifacts: List[str] = [ "azure-ai-anomalydetector", # deprecated # expect failure on below # "azure-developer-devcenter", # 2 breaks introduced into stable api-version # "azure-ai-vision-face", # SDK in development # "azure-health-insights-radiologyinsights", # SDK in development ] def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument( "--sdk-root", type=str, required=True, help="azure-sdk-for-java repository root.", ) parser.add_argument( "--package-json-path", type=str, required=True, help="path to package.json of typespec-java.", ) parser.add_argument( "--dev-package", type=str, required=False, default="false", help="use build from the branch, instead of published typespec-java.", ) return parser.parse_args() def update_emitter(package_json_path: str, use_dev_package: bool): if use_dev_package: # we cannot use "tsp-client generate-config-files" in dev mode, as this command also updates the lock file logging.info("Update emitter-package.json") subprocess.check_call( [ "pwsh", "./eng/common/scripts/typespec/New-EmitterPackageJson.ps1", "-PackageJsonPath", package_json_path, "-OutputDirectory", "eng", ], cwd=sdk_root, ) # replace version with path to dev package dev_package_path = None typespec_extension_path = os.path.dirname(package_json_path) for file in os.listdir(typespec_extension_path): if file.endswith(".tgz"): dev_package_path = os.path.abspath(os.path.join(typespec_extension_path, file)) logging.info(f'Found dev package at "{dev_package_path}"') break if dev_package_path: emitter_package_path = os.path.join(sdk_root, "eng", "emitter-package.json") with open(emitter_package_path, "r") as json_file: package_json = json.load(json_file) package_json["dependencies"]["@azure-tools/typespec-java"] = dev_package_path with open(emitter_package_path, "w") as json_file: logging.info(f'Update emitter-package.json to use typespec-java from "{dev_package_path}"') json.dump(package_json, json_file, indent=2) else: logging.error("Failed to locate the dev package.") logging.info("Update emitter-package-lock.json") subprocess.check_call(["tsp-client", "generate-lock-file"], cwd=sdk_root) else: logging.info("Update emitter-package.json and emitter-package-lock.json") subprocess.check_call( ["tsp-client", "generate-config-files", "--package-json", package_json_path], cwd=sdk_root ) def get_generated_folder_from_artifact(module_path: str, artifact: str, type: str) -> str: path = os.path.join(module_path, "src", type, "java", "com") for seg in artifact.split("-"): path = os.path.join(path, seg) path = os.path.join(path, "generated") return path def update_sdks(): failed_modules = [] for tsp_location_file in glob.glob(os.path.join(sdk_root, "sdk/*/*/tsp-location.yaml")): module_path = os.path.dirname(tsp_location_file) artifact = os.path.basename(module_path) arm_module = "-resourcemanager-" in artifact if artifact in skip_artifacts: continue generated_samples_path = os.path.join( module_path, get_generated_folder_from_artifact(module_path, artifact, "samples") ) generated_test_path = os.path.join( module_path, get_generated_folder_from_artifact(module_path, artifact, "test") ) generated_samples_exists = os.path.isdir(generated_samples_path) generated_test_exists = os.path.isdir(generated_test_path) if arm_module: logging.info("Delete source code of resourcemanager module %s", artifact) shutil.rmtree(os.path.join(module_path, "src", "main")) logging.info(f"Generate for module {artifact}") try: subprocess.check_call(["tsp-client", "update"], cwd=module_path) except subprocess.CalledProcessError: # one retry # sometimes customization have intermittent failure logging.warning(f"Retry generate for module {artifact}") try: subprocess.check_call(["tsp-client", "update", "--debug"], cwd=module_path) except subprocess.CalledProcessError: logging.error(f"Failed to generate for module {artifact}") failed_modules.append(artifact) if not arm_module: # run mvn package, as this is what's done in "TypeSpec-Compare-CurrentToCodegeneration.ps1" script subprocess.check_call(["mvn", "--no-transfer-progress", "codesnippet:update-codesnippet"], cwd=module_path) if arm_module: # revert mock test code cmd = ["git", "checkout", "src/test"] subprocess.check_call(cmd, cwd=module_path) if not generated_samples_exists: shutil.rmtree(generated_samples_path, ignore_errors=True) if not generated_test_exists: shutil.rmtree(generated_test_path, ignore_errors=True) # revert change on pom.xml, readme.md, changelog.md, etc. cmd = ["git", "checkout", "**/pom.xml"] subprocess.check_call(cmd, cwd=sdk_root) cmd = ["git", "checkout", "**/*.md"] subprocess.check_call(cmd, cwd=sdk_root) cmd = ["git", "add", "."] subprocess.check_call(cmd, cwd=sdk_root) if failed_modules: logging.error(f"Failed modules {failed_modules}") def main(): global sdk_root args = vars(parse_args()) sdk_root = args["sdk_root"] update_emitter(args["package_json_path"], args["dev_package"].lower() == "true") update_sdks() if __name__ == "__main__": logging.basicConfig( stream=sys.stdout, level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s", datefmt="%Y-%m-%d %X", ) main()