eng/versioning/sync_versions.py (156 lines of code) (raw):
import sys
import os
import logging
import argparse
import requests
import dataclasses
import re
from os import path
from typing import List, Dict
SDK_CLIENT_VERSIONS_URI: str = (
"https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/eng/versioning/version_client.txt"
)
SDK_EXTERNAL_VERSIONS_URI: str = (
"https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/eng/versioning/external_dependencies.txt"
)
EXTERNAL_DEPENDENCIES_TXT_PATH: str = "eng/versioning/external_dependencies.txt"
root_path: str = "."
@dataclasses.dataclass(frozen=True)
class Package:
group: str
artifact: str
@dataclasses.dataclass(frozen=True)
class PackageVersion:
package: Package
version: str
def load_versions(content: str) -> Dict[Package, str]:
packages = {}
for line in content.splitlines():
if not line.startswith("#"):
segments = line.split(";")
package = segments[0]
if package.startswith("testdep_"):
# fix e.g. "testdep_net.bytebuddy:byte-buddy;1.14.8"
package = package.split("_")[1]
version = None if len(segments) == 1 else segments[1]
package_segments = package.split(":")
if len(package_segments) == 2:
package_obj = Package(package_segments[0], package_segments[1])
packages[package_obj] = version
return packages
def fill_versions(
package_versions: Dict[Package, str], versions_list: List[Dict[Package, str]]
) -> List[PackageVersion]:
packages = []
for item in package_versions.items():
package = item[0]
version = item[1]
if not version:
for versions in versions_list:
if package in versions:
version = versions[package]
logging.info(f"package {package.artifact}: {version}")
break
if not version:
raise ValueError(f"version not found for package {package.group}:{package.artifact}")
packages.append(PackageVersion(package, version))
return packages
def is_xml_node(line: str, node: str) -> bool:
trimmed_line = line.strip()
return trimmed_line.startswith(f"<{node}>") and trimmed_line.endswith(f"</{node}>")
def extract_xml_node_text(line: str) -> str:
trimmed_line = line.strip()
return re.match(r"<.*?>(.*)</.*?>", trimmed_line).group(1)
def replace_xml_node_text(line: str, text: str) -> str:
return re.sub(r"(\s*<.*?>)(.*)(</.*?>\s*)", r"\g<1>" + text + r"\g<3>", line)
def update_pom(package_versions: List[PackageVersion]):
versions = {package.package: package.version for package in package_versions}
pom_files = [
"customization-base/src/main/resources/pom.xml",
"core/packages/http-client-java/generator/http-client-generator-core/src/main/resources/pom.xml",
"pom.xml",
]
for folder in os.listdir(root_path):
if path.isdir(path.join(root_path, folder)) and path.isfile(path.join(root_path, folder, "pom.xml")):
pom_files.append(path.join(folder, "pom.xml"))
for folder in os.listdir(root_path):
if path.isdir(path.join(root_path, folder)) and path.isfile(path.join(root_path, folder, "pom.xml")):
pom_files.append(path.join(folder, "pom.xml"))
core_relative_path = "core/packages/http-client-java/generator"
core_path = path.join(root_path, core_relative_path)
for folder in os.listdir(core_path):
if path.isdir(path.join(core_path, folder)) and path.isfile(path.join(core_path, folder, "pom.xml")):
pom_files.append(path.join(core_relative_path, folder, "pom.xml"))
# POMs
for pom_file in pom_files:
with open(path.join(root_path, pom_file), encoding="utf-8") as f_in:
lines = f_in.readlines()
new_lines = []
package = None
for line in lines:
if is_xml_node(line, "groupId"):
package = Package(extract_xml_node_text(line), None)
elif is_xml_node(line, "artifactId"):
if package:
package = Package(package.group, extract_xml_node_text(line))
elif is_xml_node(line, "version"):
if package and package in versions:
line = replace_xml_node_text(line, versions[package])
else:
package = None
new_lines.append(line)
if not lines == new_lines:
with open(path.join(root_path, pom_file), "w", encoding="utf-8") as f_out:
f_out.write("".join(new_lines))
logging.info(f"update POM {pom_file}")
# packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/projectmodel/Project.java
project_file = "core/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/projectmodel/Project.java"
with open(path.join(root_path, project_file)) as f_in:
lines = f_in.readlines()
new_lines = []
for line in lines:
match = re.match(r"( *[_A-Z]*\(\")(.*?)(\", \")(.*?)(\", \")(.*)(\"\)[,;]\n)", line)
if match:
package = Package(match.group(2), match.group(4))
if package in versions:
line = f"{match.group(1)}{match.group(2)}{match.group(3)}{match.group(4)}{match.group(5)}{versions[package]}{match.group(7)}"
new_lines.append(line)
if not lines == new_lines:
with open(path.join(root_path, project_file), "w", encoding="utf-8") as f_out:
f_out.write("".join(new_lines))
logging.info(f"update Project.java {project_file}")
# protocol-sdk-integration-tests/eng/versioning/version_client.txt
version_client_file = "protocol-sdk-integration-tests/eng/versioning/version_client.txt"
with open(path.join(root_path, version_client_file)) as f_in:
lines = f_in.readlines()
new_lines = []
for line in lines:
match = re.match(r"(.*?):(.*?);(.*?);(.*?)\n", line)
if match:
package = Package(match.group(1), match.group(2))
if package in versions:
version = versions[package]
line = f"{match.group(1)}:{match.group(2)};{version};{version}\n"
new_lines.append(line)
if not lines == new_lines:
with open(path.join(root_path, version_client_file), "w", encoding="utf-8") as f_out:
f_out.write("".join(new_lines))
logging.info(f"update version_client.txt {version_client_file}")
def main():
global root_path
parser = argparse.ArgumentParser(description="Sync versions with azure-sdk-for-java")
args = parser.parse_args()
script_path = path.abspath(path.dirname(sys.argv[0]))
root_path = path.abspath(path.join(script_path, "../.."))
with requests.get(SDK_CLIENT_VERSIONS_URI) as response:
client_versions = load_versions(response.content.decode("utf-8"))
with requests.get(SDK_EXTERNAL_VERSIONS_URI) as response:
external_versions = load_versions(response.content.decode("utf-8"))
with open(path.join(root_path, EXTERNAL_DEPENDENCIES_TXT_PATH), encoding="utf-8") as f_in:
package_versions = load_versions(f_in.read())
package_versions = fill_versions(package_versions, [client_versions, external_versions])
update_pom(package_versions)
if __name__ == "__main__":
logging.basicConfig(
stream=sys.stdout,
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %X",
)
main()