Last active
September 4, 2023 15:18
-
-
Save RyanJulyan/b1c3d4aeb6782ddeddcdef5693216398 to your computer and use it in GitHub Desktop.
Determine the appropriate version bump (major, minor, patch) for JSON schemas based on changes between versions. Handles attribute additions, deletions, type changes, and required fields, with special handling for 'Optional' types. Can compare multiple schema pairs to find the highest priority bump.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Any, List, Tuple, Dict, Optional | |
import subprocess | |
import sys | |
from determine_bump import determine_highest_bump | |
def bump_version( | |
schema_pairs: List[Tuple[Optional[Dict[Any, Any]], Optional[Dict[Any, Any]]]] | |
) -> None: | |
"""Bump the version based on the changes between the old and new schemas. | |
using `subprocess` to call the `bumpver` CLI command | |
Args: | |
schema_pairs (List[Tuple[Optional[Dict], Optional[Dict]]]): List of tuples containing old and new schemas for each model. | |
""" | |
bump_type: str = determine_highest_bump(schema_pairs) | |
bump_command: List[str] = ["bumpver", "update", f"--{bump_type}"] | |
result = subprocess.run( | |
bump_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE | |
) | |
if result.returncode != 0: | |
error_message = f"Error running bumpver: {result.stderr.decode()}" | |
print(error_message) | |
sys.exit(error_message) | |
else: | |
print("Version bumped successfully:", result.stdout.decode()) | |
if __name__ == "__main__": | |
schema_pairs = [ | |
( | |
{ | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
{ | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
), | |
( | |
None, | |
{ | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
), | |
( | |
{ | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
{ | |
"name": "People", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
), | |
# ... more pairs ... | |
] | |
print(bump_version(schema_pairs)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Any, List, Tuple, Dict, Optional | |
def determine_bump( | |
schema_old: Optional[Dict[Any, Any]], schema_new: Optional[Dict[Any, Any]] | |
) -> str: | |
"""Determine the version bump (major, minor, patch) based on the changes between two schemas. | |
Args: | |
schema_old (Optional[Dict[Any, Any]]): The old schema. | |
schema_new (Optional[Dict[Any, Any]]): The new schema. | |
Returns: | |
str: The type of version bump ('major', 'minor', 'patch'). | |
""" | |
if schema_new is None: | |
return "major" | |
if schema_old is None: | |
return "minor" | |
# Check for changes in 'name' property | |
if schema_old["name"] != schema_new["name"]: | |
return "major" | |
# Check for changes in top-level 'required' fields | |
if "required" in schema_old or "required" in schema_new: | |
old_required = set(schema_old.get("required", [])) | |
new_required = set(schema_new.get("required", [])) | |
if old_required != new_required: | |
return "major" # Changes in top-level required fields | |
# Check for changes in 'attributes' | |
old_attributes = {attr["name"]: attr["type"] for attr in schema_old["attributes"]} | |
new_attributes = {attr["name"]: attr["type"] for attr in schema_new["attributes"]} | |
bump_type = "patch" | |
for attr_name, attr_type in old_attributes.items(): | |
if attr_name not in new_attributes: | |
return "major" # Attribute deletion | |
new_type = new_attributes[attr_name] | |
if attr_type != new_type: | |
if "Optional" in new_type: | |
bump_type = "minor" # Change to Optional type | |
else: | |
return "major" # Other attribute type change | |
if bump_type == "minor": | |
return bump_type | |
for attr_name in new_attributes: | |
if attr_name not in old_attributes: | |
return "minor" # New attribute addition | |
return bump_type | |
def determine_highest_bump( | |
schema_pairs: List[Tuple[Optional[Dict[Any, Any]], Optional[Dict]]] | |
) -> str: | |
"""Determine the highest priority version bump (major, minor, patch) for a list of schema pairs. | |
Args: | |
schema_pairs (List[Tuple[Optional[Dict], Optional[Dict]]]): List of tuples containing old and new schemas for each model. | |
Returns: | |
str: The highest priority version bump ('major', 'minor', 'patch'). | |
""" | |
priority = {"major": 1, "minor": 2, "patch": 3} | |
highest_bump = "patch" | |
for schema_old, schema_new in schema_pairs: | |
bump = determine_bump(schema_old, schema_new) | |
if priority[bump] < priority[highest_bump]: | |
highest_bump = bump | |
if highest_bump == "major": # No need to check further | |
break | |
return highest_bump | |
if __name__ == "__main__": | |
########################## | |
##### Delete Schema: ##### | |
########################## | |
# Example usage | |
schema_old = { | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
} # Old schema | |
schema_new = None # New schema (model deletion) | |
bump = determine_bump(schema_old, schema_new) | |
print("Delete Schema:") | |
print(bump) # major | |
print() | |
############################### | |
##### Change Schema Name: ##### | |
############################### | |
# Example usage | |
schema_old = { | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
} # Old schema | |
schema_new = { | |
"name": "People", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
} # New schema (model deletion) | |
bump = determine_bump(schema_old, schema_new) | |
print("Change Schema Name:") | |
print(bump) # major | |
print() | |
######################################## | |
##### Add Non-required attributes: ##### | |
######################################## | |
# Example usage | |
schema_old = { | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
} # Old schema | |
schema_new = { | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "address2", "type": "Address2"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
} # New schema (model deletion) | |
bump = determine_bump(schema_old, schema_new) | |
print("Add Non-required attributes:") | |
print(bump) # minor | |
print() | |
###################### | |
##### New Model: ##### | |
###################### | |
# Example usage | |
schema_old = None | |
schema_new = { | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
} # New schema (model deletion) | |
bump = determine_bump(schema_old, schema_new) | |
print("New Model:") | |
print(bump) # minor | |
print() | |
###################### | |
##### No Change: ##### | |
###################### | |
# Example usage | |
schema_old = { | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
} # Old schema | |
schema_new = { | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
} # New schema (model deletion) | |
bump = determine_bump(schema_old, schema_new) | |
print("No Change:") | |
print(bump) # patch | |
print() | |
schema_pairs = [ | |
( | |
{ | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
{ | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
), | |
( | |
None, | |
{ | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
), | |
( | |
{ | |
"name": "Person", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
{ | |
"name": "People", | |
"attributes": [ | |
{"name": "name", "type": "str"}, | |
{"name": "age", "type": "int"}, | |
{"name": "hobbies", "type": "List[str]"}, | |
{"name": "address", "type": "Address"}, | |
{"name": "contact", "type": "Optional[Dict[str, str]]"}, | |
], | |
}, | |
), | |
] | |
highest_bump = determine_highest_bump(schema_pairs) | |
print(f"The highest priority version bump is: {highest_bump}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bumpver |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment