util/git-tag-check/git-tag-check.py (86 lines of code) (raw):
#!/usr/bin/env python3
import os
import subprocess
import json
import re
from typing import List, Tuple
from pathlib import Path
REMOTE_NAME = "origin"
TAG_BRANCH_JSON = Path(__file__).parent / 'tag-branch.json'
REPO_PATH = Path(__file__).parent.parent
class GitError(Exception):
pass
def run_git_command(args: List[str], error_msg: str) -> subprocess.CompletedProcess:
try:
return subprocess.run(
args,
capture_output=True,
text=True,
check=True
)
except subprocess.CalledProcessError as e:
raise GitError(f"{error_msg}: {e.stderr.strip()}")
def get_git_tags() -> List[str]:
result = run_git_command(
['git', '-C', str(REPO_PATH), 'tag', '--list'],
"Error getting tags"
)
return [tag for tag in result.stdout.strip().split('\n') if tag]
def get_commit_sha(ref: str) -> str:
result = run_git_command(
['git', '-C', str(REPO_PATH), 'rev-parse', ref],
f"Error getting SHA for {ref}"
)
return result.stdout.strip()
def verify_ref_exists(ref: str) -> None:
run_git_command(
['git', '-C', str(REPO_PATH), 'rev-parse', '--verify', ref],
f"Reference {ref} does not exist"
)
def is_tag_reachable(tag: str, branch: str) -> bool:
# Verify both references exist
verify_ref_exists(tag)
verify_ref_exists(branch)
tag_sha = get_commit_sha(tag)
branch_sha = get_commit_sha(branch)
if tag_sha != branch_sha:
# An exception will occur if the return code != 0
run_git_command(
['git', '-C', str(REPO_PATH), 'merge-base', '--is-ancestor', tag, branch],
f"Error checking if {tag} is ancestor of {branch}"
)
return True
def load_branch_tag_patterns() -> List[dict]:
try:
with open(TAG_BRANCH_JSON) as file:
patterns = json.load(file)
if not patterns:
raise Exception("Empty JSON file")
return patterns
except json.JSONDecodeError as e:
raise Exception(f"Invalid JSON file: {e}")
except IOError as e:
raise Exception(f"Error reading JSON file: {e}")
def main():
try:
branch_tag_patterns = load_branch_tag_patterns()
run_git_command(
['git', '-C', str(REPO_PATH), 'fetch', '--tags', REMOTE_NAME],
"Error fetching tags"
)
# Get tags
tags = get_git_tags()
if not tags:
raise GitError("No tags found")
# Process patterns
for item in branch_tag_patterns:
branch = item['branch']
tag_pattern = item['tag_pattern']
print(f"Processing branch: '{branch}', pattern: '{tag_pattern}'")
for tag in tags:
if re.match(tag_pattern, tag):
full_branch = f"{REMOTE_NAME}/{branch}"
if is_tag_reachable(tag, full_branch):
print(f"Tag found: {tag} on branch: {branch}")
else:
raise GitError(f"Tag NOT found: {tag} on branch: {branch}")
except GitError as e:
print(f"Error: {e}")
exit(1)
if __name__ == '__main__':
main()