diff --git a/doc/tool_usage_guide.md b/doc/tool_usage_guide.md index 8a41fcee7135..7b84516cbebe 100644 --- a/doc/tool_usage_guide.md +++ b/doc/tool_usage_guide.md @@ -27,6 +27,7 @@ This repo is currently migrating all checks from a slower `tox`-based framework, |`verifysdist`| Verify directories included in sdist and contents in manifest file. Also ensures that py.typed configuration is correct within the setup.py. | `azpysdk verifysdist .` | |`verify_keywords`| Verify that the keyword 'azure sdk' is present in the targeted package's keywords. | `azpysdk verify_keywords .` | |`import_all`| Installs the package w/ default dependencies, then attempts to `import *` from the base namespace. Ensures that all imports will resolve after a base install and import. | `azpysdk import_all .` | +|`breaking`| Checks for breaking changes. | `azpysdk breaking .` | ## Common arguments diff --git a/eng/tools/azure-sdk-tools/azpysdk/breaking.py b/eng/tools/azure-sdk-tools/azpysdk/breaking.py new file mode 100644 index 000000000000..39c736318989 --- /dev/null +++ b/eng/tools/azure-sdk-tools/azpysdk/breaking.py @@ -0,0 +1,87 @@ +import argparse +import os +import sys + +from typing import Optional, List +from subprocess import CalledProcessError, check_call + +from .Check import Check +from ci_tools.functions import install_into_venv +from ci_tools.scenario.generation import create_package_and_install +from ci_tools.variables import discover_repo_root, set_envvar_defaults +from ci_tools.logging import logger + +JSONDIFF_VERSION = "1.2.0" +REPO_ROOT = discover_repo_root() +BREAKING_CHECKER_PATH = os.path.join(REPO_ROOT, "scripts", "breaking_changes_checker") + + +class breaking(Check): + def __init__(self) -> None: + super().__init__() + + def register( + self, subparsers: "argparse._SubParsersAction", parent_parsers: Optional[List[argparse.ArgumentParser]] = None + ) -> None: + """Register the breaking change check. The breaking change check checks for breaking changes against the target package.""" + parents = parent_parsers or [] + p = subparsers.add_parser("breaking", parents=parents, help="Run the breaking change check") + p.set_defaults(func=self.run) + + def run(self, args: argparse.Namespace) -> int: + """Run the breaking change check command.""" + logger.info("Running breaking check...") + + set_envvar_defaults() + targeted = self.get_targeted_directories(args) + + results: List[int] = [] + + for parsed in targeted: + package_dir = parsed.folder + package_name = parsed.name + executable, staging_directory = self.get_executable(args.isolate, args.command, sys.executable, package_dir) + logger.info(f"Processing {package_name} for breaking check...") + + # install dependencies + self.install_dev_reqs(executable, args, package_dir) + + try: + install_into_venv( + executable, + [f"jsondiff=={JSONDIFF_VERSION}", "-e", BREAKING_CHECKER_PATH], + package_dir, + ) + except CalledProcessError as e: + logger.error( + f"Failed to install jsondiff or breaking change checker while processing {package_name}: {e}" + ) + results.append(1) + continue + + create_package_and_install( + distribution_directory=staging_directory, + target_setup=package_dir, + skip_install=False, + cache_dir=None, + work_dir=staging_directory, + force_create=False, + package_type="sdist", + pre_download_disabled=False, + python_executable=executable, + ) + + try: + cmd = [ + executable, + os.path.join(BREAKING_CHECKER_PATH, "detect_breaking_changes.py"), + "--target", + package_dir, + ] + check_call(cmd) + except CalledProcessError as e: + logger.error(f"Breaking check failed for {package_name}: {e}") + results.append(1) + continue + + return max(results) if results else 0 diff --git a/eng/tools/azure-sdk-tools/azpysdk/main.py b/eng/tools/azure-sdk-tools/azpysdk/main.py index 13643a24ccd2..dd8fac51bf79 100644 --- a/eng/tools/azure-sdk-tools/azpysdk/main.py +++ b/eng/tools/azure-sdk-tools/azpysdk/main.py @@ -28,6 +28,7 @@ from .verify_whl import verify_whl from .bandit import bandit from .verify_keywords import verify_keywords +from .breaking import breaking from ci_tools.logging import configure_logging, logger @@ -87,6 +88,7 @@ def build_parser() -> argparse.ArgumentParser: verify_whl().register(subparsers, [common]) bandit().register(subparsers, [common]) verify_keywords().register(subparsers, [common]) + breaking().register(subparsers, [common]) return parser diff --git a/scripts/breaking_changes_checker/README.md b/scripts/breaking_changes_checker/README.md index 3d1b03743ed4..15aff938e77d 100644 --- a/scripts/breaking_changes_checker/README.md +++ b/scripts/breaking_changes_checker/README.md @@ -7,6 +7,21 @@ The breaking changes tool compares the last stable/GA version of the library (if Add your package name to the `RUN_BREAKING_CHANGES_PACKAGES` found [here](https://github.com/Azure/azure-sdk-for-python/tree/main/scripts/breaking_changes_checker/breaking_changes_allowlist.py). +## Run locally with `azpysdk` + +**1) Install azpysdk:** + +`pip install -e eng/tools/azure-sdk-tools[build]` + +**2) Run the `breaking` check.** + +Here we run the breaking changes tool against azure-storage-blob, for example: + +```bash +cd ./sdk/storage/azure-storage-blob +azpysdk breaking . +``` + ## Run locally with tox **1) Install tox:**