From b33688e19f6033a8072705125ff36ab6cb4ecd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20B=C3=A4ck?= Date: Fri, 3 Jan 2025 16:14:00 +0100 Subject: [PATCH] Add bump_definitions.py The new script makes it much easier to create new versions of one or more types. For example, ./bump_definitions.py minor 'EiffelActivity*Event' creates new minor versions of all activity events. --- bump_definitions.py | 106 +++++++++++++++++++++++++++++++++++++++ test_bump_definitions.py | 82 ++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100755 bump_definitions.py create mode 100644 test_bump_definitions.py diff --git a/bump_definitions.py b/bump_definitions.py new file mode 100755 index 00000000..b8c3e772 --- /dev/null +++ b/bump_definitions.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +# Copyright 2025 Axis Communications AB. +# For a full list of individual contributors, please see the commit history. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Creates new versions of existing type definitions by increasing the +desired version position (major, minor, or patch) and creating a new +matching file. +""" + +import argparse +import fnmatch +import re +import sys +import time +from pathlib import Path +from typing import List + +import semver + +import versions + + +def _bump_versions(root: Path, position: str, pattern: str) -> List[Path]: + """Find definition files in the given directory, match their types + against the given glob pnattern, and create new files with version + bumps in the selected position. Returns the paths of the created + files. + """ + result = [] + for typename, version in versions.latest_in_dir(root).items(): + if not fnmatch.fnmatch(typename, pattern): + continue + + old_path = root / typename / f"{version}.yml" + new_version = getattr(version, "bump_" + position)() + new_path = old_path.with_name(f"{new_version}.yml") + new_path.write_text( + _transform_definition(old_path.read_text(encoding="utf-8"), new_version), + encoding="utf-8", + ) + result.append(new_path) + return result + + +def _transform_definition( + old_definition: str, version: semver.version.Version, copyright_year: int = None +) -> str: + """Return an updated type definition with the new version and + copyright year patched in. + """ + result = re.sub( + r"^_version: (.*)$", f"_version: {version}", old_definition, flags=re.M + ) + + current_year = copyright_year or time.localtime(time.time()).tm_year + for expr, repl in ( + # Replace the end year in a range. + ( + r"^# Copyright (20\d\d)-20\d\d ", + f"# Copyright \\1-{current_year} ", + ), + # Turn a single year into a range. + ( + r"^# Copyright (20\d\d) ", + f"# Copyright \\1-{current_year} ", + ), + # Transform nonsense ranges on the form 2024-2024 back into a single year. + ( + f"^# Copyright {current_year}-{current_year} ", + f"# Copyright {current_year} ", + ), + ): + result = re.sub(expr, repl, result, flags=re.M) + + return result + + +if __name__ == "__main__": + argparser = argparse.ArgumentParser(description=__doc__) + argparser.add_argument( + "position", + metavar="POSITION", + choices=("major", "minor", "patch"), + help="the version position that should be increased", + ) + argparser.add_argument( + "pattern", + metavar="TYPE_PATTERN", + help="XXX", + ) + args = argparser.parse_args(sys.argv[1:]) + for path in _bump_versions(Path("definitions"), args.position, args.pattern): + print(path) diff --git a/test_bump_definitions.py b/test_bump_definitions.py new file mode 100644 index 00000000..4e45a090 --- /dev/null +++ b/test_bump_definitions.py @@ -0,0 +1,82 @@ +# Copyright 2025 Axis Communications AB. +# For a full list of individual contributors, please see the commit history. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re + +import semver + +import bump_definitions + + +def _strip_leading_indent(s): + return re.sub(r"^\s+", "", s, flags=re.M) + + +def test_transform_definition_replaces_version(): + assert ( + bump_definitions._transform_definition( + _strip_leading_indent( + """ + # Copyright blah + _version: 1.0.0 + """ + ), + semver.VersionInfo.parse("2.0.0"), + ) + == _strip_leading_indent( + """ + # Copyright blah + _version: 2.0.0 + """ + ) + ) + + +def test_transform_definition_updates_range(): + assert ( + bump_definitions._transform_definition( + _strip_leading_indent( + """ + # Copyright 2020-2021 Company Name, Inc. + """ + ), + semver.VersionInfo.parse("2.0.0"), + copyright_year=2025, + ) + == _strip_leading_indent( + """ + # Copyright 2020-2025 Company Name, Inc. + """ + ) + ) + + +def test_transform_definition_turn_year_into_range(): + assert ( + bump_definitions._transform_definition( + _strip_leading_indent( + """ + # Copyright 2020 Company Name, Inc. + """ + ), + semver.VersionInfo.parse("2.0.0"), + copyright_year=2025, + ) + == _strip_leading_indent( + """ + # Copyright 2020-2025 Company Name, Inc. + """ + ) + )