Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Assess and upgrade Azure workloads between plans, tiers, or SKUs with automated migration steps
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/languages/java/scripts/upgrade_bom.py
1#!/usr/bin/env python32"""Upgrade or add azure-sdk-bom in a Maven or Gradle project using OpenRewrite.34Detects the build system automatically and applies the appropriate OpenRewrite5recipes. All operations use OpenRewrite — no manual XML/text manipulation.67Step 1 – Add or upgrade BOM:8- Maven (add): org.openrewrite.maven.AddManagedDependency9- Maven (upgrade): org.openrewrite.maven.UpgradeDependencyVersion10- Gradle (add): org.openrewrite.gradle.AddPlatformDependency11- Gradle (upgrade):org.openrewrite.gradle.UpgradeDependencyVersion1213Step 2 – Remove redundant explicit versions:14- Maven: org.openrewrite.maven.RemoveRedundantDependencyVersions15- Gradle: org.openrewrite.gradle.RemoveRedundantDependencyVersions1617Usage:18python3 upgrade_bom.py <project_dir> <bom_version> [options]19python3 upgrade_bom.py --get-latest-version2021Arguments:22project_dir Path to the project root (must contain pom.xml or build.gradle).23bom_version Target azure-sdk-bom version to apply. Resolve the latest stable version first.2425Options:26--mvn <cmd> Maven command override.27--gradle <cmd> Gradle command override.28"""2930from __future__ import annotations3132import argparse33import os34import re35import stat36import subprocess37import sys38import textwrap39import urllib.error40import urllib.request41import xml.etree.ElementTree as ET4243GROUP_ID = "com.azure"44ARTIFACT_ID = "azure-sdk-bom"45BOM_POM_URL = "https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/boms/azure-sdk-bom/pom.xml"46POM_NAMESPACE = {"m": "http://maven.apache.org/POM/4.0.0"}4748# Maven constants49MVN_REWRITE_PLUGIN = "org.openrewrite.maven:rewrite-maven-plugin"50MVN_REWRITE_ARTIFACT_COORDS = "org.openrewrite:rewrite-maven"51MVN_UPGRADE_RECIPE = "org.openrewrite.maven.UpgradeDependencyVersion"52MVN_ADD_MANAGED_RECIPE = "org.openrewrite.maven.AddManagedDependency"53MVN_REMOVE_REDUNDANT_RECIPE = "org.openrewrite.maven.RemoveRedundantDependencyVersions"5455# Gradle constants56GRADLE_UPGRADE_RECIPE = "org.openrewrite.gradle.UpgradeDependencyVersion"57GRADLE_ADD_PLATFORM_RECIPE = "org.openrewrite.gradle.AddPlatformDependency"58GRADLE_REMOVE_REDUNDANT_RECIPE = "org.openrewrite.gradle.RemoveRedundantDependencyVersions"59REWRITE_YML_NAME = "rewrite.yml"60GRADLE_PLUGIN_MARKER = "// --- openrewrite-upgrade-bom-plugin (auto-added, safe to remove) ---"6162# ---------------------------------------------------------------------------63# Build-system detection64# ---------------------------------------------------------------------------6566def _detect_build_system(project_dir: str) -> str:67"""Return 'maven' or 'gradle' depending on which build file is present."""68if os.path.isfile(os.path.join(project_dir, "pom.xml")):69return "maven"70for name in ("build.gradle", "build.gradle.kts"):71if os.path.isfile(os.path.join(project_dir, name)):72return "gradle"73return "unknown"747576def _get_latest_bom_version() -> str:77try:78with urllib.request.urlopen(BOM_POM_URL) as response:79pom_xml = response.read()80except urllib.error.URLError as exc:81raise SystemExit(f"Failed to download {BOM_POM_URL}: {exc}") from exc8283try:84root = ET.fromstring(pom_xml)85except ET.ParseError as exc:86raise SystemExit(f"Failed to parse BOM pom.xml: {exc}") from exc8788version = root.findtext("m:version", namespaces=POM_NAMESPACE)89if not version:90raise SystemExit("Failed to find the azure-sdk-bom <version> in pom.xml")9192return version.strip()939495# ---------------------------------------------------------------------------96# Maven helpers97# ---------------------------------------------------------------------------9899def _detect_maven(project_dir: str) -> str:100if sys.platform == "win32":101wrapper = os.path.join(project_dir, "mvnw.cmd")102# .cmd files on Windows are invoked by the shell; no executable bit needed.103if os.path.isfile(wrapper):104return wrapper105else:106wrapper = os.path.join(project_dir, "mvnw")107if os.path.isfile(wrapper):108if not os.access(wrapper, os.X_OK):109# Wrapper exists but isn't executable (common after fresh clones110# on filesystems that don't preserve the +x bit). Try to fix it.111try:112mode = os.stat(wrapper).st_mode113os.chmod(wrapper, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)114print(f"[upgrade_bom] Added executable bit to {wrapper}.")115except OSError as exc:116print(117f"[upgrade_bom] WARNING: mvnw exists at {wrapper} but is not "118f"executable and chmod failed ({exc}); falling back to 'mvn'.",119file=sys.stderr,120)121return "mvn"122if os.access(wrapper, os.X_OK):123return wrapper124return "mvn"125126127def _has_maven_bom_entry(pom_path: str) -> bool:128try:129tree = ET.parse(pom_path)130except ET.ParseError:131return False132ns = {"m": "http://maven.apache.org/POM/4.0.0"}133for dep in tree.findall(".//m:dependencyManagement/m:dependencies/m:dependency", ns):134gid = dep.find("m:groupId", ns)135aid = dep.find("m:artifactId", ns)136if gid is not None and aid is not None:137if gid.text == GROUP_ID and aid.text == ARTIFACT_ID:138return True139for dep in tree.findall(".//dependencyManagement/dependencies/dependency"):140gid = dep.find("groupId")141aid = dep.find("artifactId")142if gid is not None and aid is not None:143if gid.text == GROUP_ID and aid.text == ARTIFACT_ID:144return True145return False146147148def _run_maven_recipe(mvn_cmd: str, project_dir: str, recipe: str, options: str) -> int:149"""Run an OpenRewrite recipe via the rewrite-maven-plugin."""150cmd = [151mvn_cmd, "-U",152f"{MVN_REWRITE_PLUGIN}:run",153f"-Drewrite.recipeArtifactCoordinates={MVN_REWRITE_ARTIFACT_COORDS}",154f"-Drewrite.activeRecipes={recipe}",155f"-Drewrite.options={options}",156]157print(f"[upgrade_bom] Running: {' '.join(cmd)}")158return subprocess.run(cmd, cwd=project_dir).returncode159160161def _handle_maven(project_dir: str, bom_version: str, mvn_cmd: str | None) -> int:162pom_path = os.path.join(project_dir, "pom.xml")163mvn = mvn_cmd or _detect_maven(project_dir)164165# Step 1: Add or upgrade the BOM166if not _has_maven_bom_entry(pom_path):167print("[upgrade_bom] No existing azure-sdk-bom entry found — adding via AddManagedDependency.")168options = ",".join([169f"groupId={GROUP_ID}",170f"artifactId={ARTIFACT_ID}",171f"version={bom_version}",172"type=pom",173"scope=import",174])175rc = _run_maven_recipe(mvn, project_dir, MVN_ADD_MANAGED_RECIPE, options)176if rc != 0:177print(f"[upgrade_bom] ERROR: AddManagedDependency exited with code {rc}", file=sys.stderr)178return rc179print(f"[upgrade_bom] azure-sdk-bom {bom_version} added successfully.")180else:181print(f"[upgrade_bom] Existing azure-sdk-bom entry found — upgrading to {bom_version}.")182options = ",".join([183f"groupId={GROUP_ID}",184f"artifactId={ARTIFACT_ID}",185f"newVersion={bom_version}",186"overrideManagedVersion=true",187])188rc = _run_maven_recipe(mvn, project_dir, MVN_UPGRADE_RECIPE, options)189if rc != 0:190print(f"[upgrade_bom] ERROR: UpgradeDependencyVersion exited with code {rc}", file=sys.stderr)191return rc192print(f"[upgrade_bom] azure-sdk-bom upgraded to {bom_version} successfully.")193194# Step 2: Remove explicit versions from Azure deps managed by the BOM195print("[upgrade_bom] Removing redundant explicit versions for Azure dependencies...")196options = f"groupPattern={GROUP_ID}*,onlyIfManagedVersionIs=GTE"197rc = _run_maven_recipe(mvn, project_dir, MVN_REMOVE_REDUNDANT_RECIPE, options)198if rc != 0:199print(f"[upgrade_bom] WARNING: RemoveRedundantDependencyVersions exited with code {rc}", file=sys.stderr)200else:201print("[upgrade_bom] Redundant explicit versions removed successfully.")202return rc203204205# ---------------------------------------------------------------------------206# Gradle helpers207# ---------------------------------------------------------------------------208209def _detect_gradle(project_dir: str) -> str:210if sys.platform == "win32":211wrapper = os.path.join(project_dir, "gradlew.bat")212if os.path.isfile(wrapper):213return wrapper214else:215wrapper = os.path.join(project_dir, "gradlew")216if os.path.isfile(wrapper):217if not os.access(wrapper, os.X_OK):218try:219mode = os.stat(wrapper).st_mode220os.chmod(wrapper, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)221print(f"[upgrade_bom] Added executable bit to {wrapper}.")222except OSError as exc:223print(224f"[upgrade_bom] WARNING: gradlew exists at {wrapper} but is not "225f"executable and chmod failed ({exc}); falling back to 'gradle'.",226file=sys.stderr,227)228return "gradle"229if os.access(wrapper, os.X_OK):230return wrapper231return "gradle"232233234def _find_gradle_build_file(project_dir: str) -> str | None:235for name in ("build.gradle", "build.gradle.kts"):236path = os.path.join(project_dir, name)237if os.path.isfile(path):238return path239return None240241242def _is_kotlin_dsl(build_file: str) -> bool:243return build_file.endswith(".kts")244245246def _has_gradle_bom_entry(build_file: str) -> bool:247"""Check whether build.gradle already references azure-sdk-bom."""248try:249with open(build_file, "r", encoding="utf-8") as f:250content = f.read()251except OSError:252return False253return f"{GROUP_ID}:{ARTIFACT_ID}" in content254255256def _create_rewrite_yml(project_dir: str, bom_version: str, has_bom: bool) -> str:257"""Create a temporary rewrite.yml with the appropriate OpenRewrite recipes.258259When has_bom is True, uses UpgradeDependencyVersion to upgrade the existing BOM.260When has_bom is False, uses AddPlatformDependency to add a new enforcedPlatform BOM.261Always includes RemoveRedundantDependencyVersions as a final step.262"""263yml_path = os.path.join(project_dir, REWRITE_YML_NAME)264recipes: list[str] = []265266if has_bom:267recipes.append(textwrap.dedent(f"""\268- org.openrewrite.gradle.UpgradeDependencyVersion:269groupId: {GROUP_ID}270artifactId: {ARTIFACT_ID}271newVersion: {bom_version}"""))272else:273recipes.append(textwrap.dedent(f"""\274- org.openrewrite.gradle.AddPlatformDependency:275groupId: {GROUP_ID}276artifactId: {ARTIFACT_ID}277version: {bom_version}278configuration: implementation279enforced: true"""))280281recipes.append(textwrap.dedent(f"""\282- org.openrewrite.gradle.RemoveRedundantDependencyVersions:283groupPattern: {GROUP_ID}*284onlyIfManagedVersionIs: GTE"""))285286yml_content = textwrap.dedent("""\287---288type: specs.openrewrite.org/v1beta/recipe289name: com.azure.UpgradeBom290displayName: Upgrade azure-sdk-bom and remove redundant versions291recipeList:292""") + "\n".join(recipes) + "\n"293294with open(yml_path, "w", encoding="utf-8") as f:295f.write(yml_content)296print(f"[upgrade_bom] Created {yml_path}")297return yml_path298299300def _inject_gradle_rewrite_plugin(build_file: str) -> bool:301"""Temporarily add the OpenRewrite plugin to build.gradle if not present.302303Returns True if the plugin block was injected (and should be cleaned up).304"""305with open(build_file, "r", encoding="utf-8") as f:306content = f.read()307308if "org.openrewrite.rewrite" in content:309return False310311kotlin = _is_kotlin_dsl(build_file)312if kotlin:313plugin_line = ' id("org.openrewrite.rewrite") version "latest.release"'314else:315plugin_line = ' id "org.openrewrite.rewrite" version "latest.release"'316317rewrite_block_kt = textwrap.dedent("""\318319rewrite {320activeRecipe("com.azure.UpgradeBom")321}322323repositories {324mavenCentral()325}326""")327rewrite_block_groovy = rewrite_block_kt # same syntax for both DSLs here328329plugins_pattern = re.compile(r"(plugins\s*\{)", re.MULTILINE)330match = plugins_pattern.search(content)331if match:332insert_pos = match.end()333# content[insert_pos:] already starts with the newline that follows334# `plugins {`, so don't add another one before the marker.335content = (336content[:insert_pos]337+ "\n"338+ GRADLE_PLUGIN_MARKER339+ "\n"340+ plugin_line341+ content[insert_pos:]342)343else:344# No plugins block — prepend one345content = (346"plugins {\n"347+ GRADLE_PLUGIN_MARKER348+ "\n"349+ plugin_line350+ "\n}\n\n"351+ content352)353354content += GRADLE_PLUGIN_MARKER + "\n"355content += rewrite_block_kt if kotlin else rewrite_block_groovy356357with open(build_file, "w", encoding="utf-8") as f:358f.write(content)359print(f"[upgrade_bom] Injected OpenRewrite plugin into {build_file}")360return True361362363def _remove_gradle_rewrite_plugin(build_file: str) -> None:364"""Remove the temporarily injected OpenRewrite plugin and config blocks."""365with open(build_file, "r", encoding="utf-8") as f:366lines = f.readlines()367368cleaned: list[str] = []369marker_count = 0370i = 0371while i < len(lines):372line = lines[i]373if GRADLE_PLUGIN_MARKER in line:374marker_count += 1375if marker_count == 1:376# First marker (inside plugins {}): skip the marker line and377# the following injected plugin id line.378i += 2379continue380else:381# Second marker (at end of file): skip the marker and every382# remaining line — they're the injected rewrite {} and383# repositories {} blocks.384break385cleaned.append(line)386i += 1387388with open(build_file, "w", encoding="utf-8") as f:389f.writelines(cleaned)390print(f"[upgrade_bom] Cleaned up OpenRewrite plugin from {build_file}")391392393def _run_gradle_openrewrite(gradle_cmd: str, project_dir: str) -> int:394cmd = [gradle_cmd, "rewriteRun"]395print(f"[upgrade_bom] Running: {' '.join(cmd)}")396return subprocess.run(cmd, cwd=project_dir).returncode397398399def _handle_gradle(project_dir: str, bom_version: str, gradle_cmd: str | None) -> int:400build_file = _find_gradle_build_file(project_dir)401if build_file is None:402print("[upgrade_bom] ERROR: no build.gradle or build.gradle.kts found", file=sys.stderr)403return 1404405gradle = gradle_cmd or _detect_gradle(project_dir)406has_bom = _has_gradle_bom_entry(build_file)407408if has_bom:409print(f"[upgrade_bom] Existing azure-sdk-bom entry found — upgrading to {bom_version}.")410else:411print("[upgrade_bom] No existing azure-sdk-bom entry found — adding via AddPlatformDependency.")412413yml_path = None414injected = False415416try:417# Set up OpenRewrite: create rewrite.yml + inject plugin temporarily418yml_path = _create_rewrite_yml(project_dir, bom_version, has_bom=has_bom)419injected = _inject_gradle_rewrite_plugin(build_file)420rc = _run_gradle_openrewrite(gradle, project_dir)421finally:422if yml_path and os.path.isfile(yml_path):423os.remove(yml_path)424print(f"[upgrade_bom] Removed {yml_path}")425if injected:426_remove_gradle_rewrite_plugin(build_file)427428if rc != 0:429print(f"[upgrade_bom] ERROR: OpenRewrite exited with code {rc}", file=sys.stderr)430else:431print(f"[upgrade_bom] BOM set to {bom_version} and redundant versions removed successfully.")432return rc433434435# ---------------------------------------------------------------------------436# Main437# ---------------------------------------------------------------------------438439def main(argv: list[str] | None = None) -> int:440parser = argparse.ArgumentParser(441description="Upgrade azure-sdk-bom version in a Maven or Gradle project using OpenRewrite."442)443parser.add_argument("project_dir", nargs="?", help="Path to the project root.")444parser.add_argument(445"bom_version",446nargs="?",447help="Target azure-sdk-bom version to apply. Resolve the latest stable version first.",448)449parser.add_argument("--mvn", default=None, help="Maven command override.")450parser.add_argument("--gradle", default=None, help="Gradle command override.")451parser.add_argument(452"--get-latest-version",453action="store_true",454help="Print the latest azure-sdk-bom version from the Azure SDK for Java BOM pom.xml.",455)456args = parser.parse_args(argv)457458if args.get_latest_version:459if args.project_dir or args.bom_version:460parser.error("--get-latest-version does not accept project_dir or bom_version.")461print(_get_latest_bom_version())462return 0463464if not args.project_dir or not args.bom_version:465parser.error("project_dir and bom_version are required unless --get-latest-version is used.")466467project_dir = os.path.abspath(args.project_dir)468build_system = _detect_build_system(project_dir)469470if build_system == "maven":471print("[upgrade_bom] Detected Maven project.")472return _handle_maven(project_dir, args.bom_version, args.mvn)473elif build_system == "gradle":474print("[upgrade_bom] Detected Gradle project.")475return _handle_gradle(project_dir, args.bom_version, args.gradle)476else:477print(478f"[upgrade_bom] ERROR: No pom.xml or build.gradle found in {project_dir}",479file=sys.stderr,480)481return 1482483484if __name__ == "__main__":485sys.exit(main())486