Skip to content

Instantly share code, notes, and snippets.

@RyanJulyan
Last active September 4, 2023 15:18
Show Gist options
  • Save RyanJulyan/b1c3d4aeb6782ddeddcdef5693216398 to your computer and use it in GitHub Desktop.
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.
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))
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}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment