Skip to content

Commit

Permalink
Change: Update java version command with config file (#886)
Browse files Browse the repository at this point in the history
Introduce a config file for updating Java specific version files.
  • Loading branch information
stefanTolksdorf committed Sep 28, 2023
1 parent c2a9948 commit fd7af60
Show file tree
Hide file tree
Showing 2 changed files with 413 additions and 468 deletions.
279 changes: 120 additions & 159 deletions pontos/version/commands/_java.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,199 +15,160 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import logging
import json
import re
from pathlib import Path
from typing import Literal, Optional, Union

from lxml import etree
from typing import Literal, Union, Dict, List, Any

from ._command import VersionCommand
from ..errors import VersionError
from ..schemes import PEP440VersioningScheme
from ..version import Version, VersionUpdate
from ._command import VersionCommand

TEMPLATE = """# pylint: disable=invalid-name
# THIS IS AN AUTOGENERATED FILE. DO NOT TOUCH!
__version__ = "{}"\n"""

VERSION_PATTERN = (
r"([0-9]+\.[0-9]+\.[0-9]+(-?([ab]|rc|alpha|beta)[0-9]+(.dev[0-9]+)?)?)"
r"^(?P<pre>.*[^\d])"
r"(?P<version>\d+\.\d+\.\d+"
r"(-?([ab]|rc|alpha|beta)\d+(.dev\d+)?)?)"
r"(?P<post>.*$)"
)


def find_file(
filename: Path, search_path: str, search_glob: str
) -> Optional[Path]:
"""Find a file somewhere within an directory tree
Arg:
filename: The file to look up
search_path: The path to look for the file
search_glob: The glob search pattern
Returns:
The file as Path object, if existing
"""
search_path = Path(search_path).resolve()
for file_path in search_path.glob(search_glob):
if file_path.is_file() and file_path.name == filename.name:
return file_path
logging.warning("File %s not found in %s.", filename.name, search_path)
return None


def replace_string_in_file(
file_path: Path, pattern: str, replacement: str
) -> None:
# Read the content of the file
content = file_path.read_text(encoding="utf-8")

# Search for the pattern in the content
match = re.search(pattern, content)

# Replace the matched group (Group 1) with the replacement
if match:
# Write the updated content back to the file
file_path.write_text(
content.replace(match.group(1), replacement), encoding="utf-8"
)
else:
logging.warning(
"Couldn't match the pattern %s in the content of %s.",
pattern,
file_path,
)
logging.warning("Content: %s", content)


# This class is used for Java Version command(s)
class JavaVersionCommand(VersionCommand):
project_file_name = "pom.xml"
_properties_file_path = Path("src/main/resources/application.properties")
_pom_xml: Optional[etree.Element] = None

def _get_version_from_pom_xml(self) -> Version:
"""
Return the version information from the <version> tag of the
pom.xml file. The version may be in non standardized form.
"""

pom_xml: etree.Element = self.pom_xml

version_element = pom_xml.find("{*}version")
if version_element is None:
raise VersionError("Version tag missing in pom.xml")

return PEP440VersioningScheme.parse_version(version_element.text)

def _update_pom_version(
self,
new_version: Version,
) -> None:
"""
Update the version in the pom.xml file
"""
pom_xml: etree.Element = self.pom_xml

version_element = pom_xml.find("{*}version")
if version_element is None:
raise VersionError("Version tag missing in pom.xml")
version_element.text = str(new_version)

etree.ElementTree(pom_xml).write(
self.project_file_path, pretty_print=True, encoding="utf-8"
)
project_file_name = "upgradeVersion.json"

def _update_properties_file(
self,
new_version: Version,
) -> None:
# update the java properties file version
if not self._properties_file_path.exists():
# skip if not existing
return
pattern = rf"sentry\.release={VERSION_PATTERN}"
replace_string_in_file(
self._properties_file_path,
pattern=pattern,
replacement=str(new_version),
)

def _update_swagger_config(
self,
new_version: Version,
) -> None:
# update swagger config file version
swagger_config_file = find_file(
filename=Path("SwaggerConfig.java"),
search_path="src",
search_glob="**/config/swagger/*",
)
if not swagger_config_file:
# skip if not existing
return
pattern = rf'\.version\("{VERSION_PATTERN}"\)'
replace_string_in_file(
swagger_config_file, pattern=pattern, replacement=str(new_version)
)

@property
def pom_xml(self) -> etree.Element:
if self._pom_xml is not None:
return self._pom_xml

if not self.project_file_path.exists():
raise VersionError("pom.xml file not found.")

try:
pom_xml: etree.ElementTree = etree.parse(self.project_file_path)
except etree.XMLSyntaxError as e:
raise VersionError(e) from e
def get_current_version(self) -> Version:
file_versions = self._read_versions_from_files()

self._pom_xml = pom_xml.getroot()
last_version = self._verify_version(file_versions)

return self._pom_xml
if last_version == "":
raise VersionError("no version found")

def get_current_version(self) -> Version:
"""Get the current version of this project
In go the version is only defined within the repository
tags, thus we need to check git, what tag is the latest"""
return self._get_version_from_pom_xml()
return self.versioning_scheme.parse_version(last_version)

def verify_version(
self, version: Union[Literal["current"], Version, None]
) -> None:
"""Verify the current version of this project"""
current_version = self.get_current_version()
file_versions = self._read_versions_from_files()

last_version = self._verify_version(file_versions)

if current_version != version:
if last_version != str(version):
raise VersionError(
f"Provided version {version} does not match the "
f"current version {current_version} in "
f"{self.project_file_path}."
f"current version {last_version} "
f"in '{self.project_file_path}'"
)

def update_version(
self, new_version: Version, *, force: bool = False
) -> VersionUpdate:
try:
package_version = self.get_current_version()
if not force and new_version == package_version:
return VersionUpdate(previous=package_version, new=new_version)
current_version = self.get_current_version()
if not force and new_version == current_version:
return VersionUpdate(previous=current_version, new=new_version)
except VersionError:
# just ignore current version and override it
package_version = None
current_version = None

changed_files = [self.project_file_path]
self._update_pom_version(new_version=new_version)
self._update_properties_file(new_version=new_version)
self._update_swagger_config(new_version=new_version)
changed_files = self._update_version_files(new_version)

return VersionUpdate(
previous=package_version,
previous=current_version,
new=new_version,
changed_files=changed_files,
)

def _update_version_files(self, new_version) -> List[Path]:
config = self._load_config()

changed_files: List[Path] = []
for file_config in config["files"]:
file_path = file_config["path"]
with (Path.cwd() / file_path).open("r") as input_file_handle:
lines = input_file_handle.readlines()
line_number = file_config["line"]
version_line = lines[line_number - 1]

matches = re.match(VERSION_PATTERN, version_line, re.DOTALL)
if matches is None:
raise VersionError(
f"Line has no version, "
f"file:'{file_path}' "
f"lineNo:{line_number} "
f"content:'{version_line}'"
)
lines[line_number - 1] = (
matches.group("pre")
+ str(new_version)
+ matches.group("post")
)

content = "".join(lines)
with (Path.cwd() / file_path).open("w") as output_file_handle:
output_file_handle.write(content)
changed_files.append(Path(file_config["path"]))
return changed_files

def _load_config(self) -> Dict[str, Any]:
version_config_file = Path.cwd() / "upgradeVersion.json"
if not version_config_file.exists():
raise VersionError(
f"No {version_config_file} config file found. "
"This file is required for pontos"
)

with version_config_file.open("r") as f:
json_string = f.read()
config = json.loads(json_string)
return config

def _read_versions_from_files(self) -> Dict[str, str]:
config = self._load_config()

file_versions = {}
for file_config in config["files"]:
file_path = file_config["path"]
file = Path.cwd() / file_path
if not file.exists():
raise VersionError(f"No {file} file found.")

with file.open("r") as f:
line_number = file_config["line"]
readlines = f.readlines()
if line_number - 1 > len(readlines):
raise VersionError(
f"Line number:{line_number} "
f"is beyond file lines:{len(readlines) + 1} "
f"file:'{file_path}'"
)
version_line = readlines[line_number - 1]
matches = re.match(VERSION_PATTERN, version_line, re.DOTALL)
if matches is None:
raise VersionError(
f"Line has no version, "
f"file:'{file_path}' "
f"lineNo:{line_number} "
f"content:'{version_line}'"
)
file_versions[file_path] = matches.group("version")
return file_versions

def _verify_version(self, file_versions: Dict[str, str]) -> str:
last_version = ""
last_file_name = ""
for file_name, version in file_versions.items():
if last_version == "":
last_version = version
last_file_name = file_name
continue

if last_version != version:
raise VersionError(
f"Versions are not the same "
f"last_file_name:'{last_file_name}' "
f"last_version:'{last_version}' "
f"file_name:'{file_name}' "
f"version:'{version}'"
)
return last_version
Loading

0 comments on commit fd7af60

Please sign in to comment.