feat: enhance GitHub release tracking with pattern support and version extraction
Build / build-8 (pull_request) Failing after 2m12s
Build / build-9 (pull_request) Failing after 4m45s

- Add github_release_pattern field to support multi-product repositories
- Implement get_github_releases_by_pattern() for filtered release matching
- Add parse_github_version_release() to extract version-release components
- Update openbao plugin packages with release patterns and correct versions
- Configure openbao-plugins meta package for manual versioning
- Fix HashiCorp package GitHub repository references
- Support complex tag formats like "secrets-consul-v0.1.0" and "v7.1.3-1"

This enables automatic updates for packages sharing GitHub repos while
maintaining proper RPM version/release semantics and backward compatibility.
This commit is contained in:
2025-12-30 21:22:48 +11:00
parent 1e7b06aa84
commit bf33bd16eb
37 changed files with 737 additions and 538 deletions
+128 -4
View File
@@ -285,6 +285,32 @@ def normalize_github_version(version: str) -> str:
return version
def parse_github_version_release(version: str) -> tuple[str, str]:
"""
Parse GitHub version string and extract version and release components.
Args:
version: Version string (e.g., "v7.1.3-1" or "v1.2.3")
Returns:
Tuple of (version, release_suffix) where release_suffix is empty if no trailing number
"""
import re
# Remove 'v' prefix if present
if version.startswith('v'):
version = version[1:]
# Check if version ends with -<number>
match = re.match(r'^(.+)-(\d+)$', version)
if match:
version_part = match.group(1)
release_suffix = match.group(2)
return version_part, release_suffix
else:
return version, ""
def compare_versions(current: str, latest: str) -> bool:
"""
Compare version strings to determine if latest is newer.
@@ -311,6 +337,69 @@ def compare_versions(current: str, latest: str) -> bool:
return latest != current
def get_github_releases_by_pattern(repo: str, pattern: str) -> Optional[dict]:
"""
Get the latest release from GitHub API that matches a specific pattern.
Args:
repo: GitHub repository in format "owner/repo"
pattern: Regex pattern to match release tag names
Returns:
Latest matching release info or None if not found
"""
import re
logger = logging.getLogger(__name__)
try:
github_token = get_github_token()
url = f"https://api.github.com/repos/{repo}/releases"
headers = {
'Authorization': f'token {github_token}',
'Accept': 'application/vnd.github.v3+json'
}
logger.debug(f"Checking GitHub releases with pattern '{pattern}': {url}")
response = requests.get(url, headers=headers, timeout=30)
if response.status_code == 200:
releases = response.json()
# Filter releases by pattern and find the latest
matching_releases = []
for release in releases:
tag_name = release.get('tag_name', '')
if re.match(pattern, tag_name):
matching_releases.append(release)
if matching_releases:
# Return the first (most recent) matching release
latest_release = matching_releases[0]
logger.debug(f"Latest matching release for {repo} with pattern '{pattern}': {latest_release.get('tag_name', 'unknown')}")
return latest_release
else:
logger.warning(f"No releases matching pattern '{pattern}' found for {repo}")
return None
elif response.status_code == 404:
logger.warning(f"No releases found for {repo}")
return None
elif response.status_code == 401:
logger.error("GitHub authentication failed. Check GitHub token.")
return None
else:
logger.warning(
f"Unexpected response from GitHub API for {repo}: "
f"{response.status_code} - {response.text}"
)
return None
except requests.RequestException as e:
logger.error(f"Failed to check GitHub releases for {repo}: {e}")
return None
def get_github_token() -> str:
"""
Retrieve GitHub API token from Vault.
@@ -1048,6 +1137,7 @@ class Builder:
license=metadata_data.get('license', ''),
builds=builds
)
github_release_pattern = metadata_data.get('github_release_pattern', '')
if not package_metadata.github:
self.logger.debug(f"Package {package_name} has no GitHub repo configured")
@@ -1055,12 +1145,35 @@ class Builder:
self.logger.info(f"Checking {package_name} from {package_metadata.github}")
# Get latest release from GitHub
latest_release = get_github_latest_release(package_metadata.github)
# Get latest release from GitHub, with pattern filtering if specified
if github_release_pattern:
self.logger.debug(f"Using release pattern: {github_release_pattern}")
latest_release = get_github_releases_by_pattern(package_metadata.github, github_release_pattern)
else:
latest_release = get_github_latest_release(package_metadata.github)
if not latest_release:
return False
latest_version = normalize_github_version(latest_release.get('tag_name', ''))
# Parse version and release from GitHub tag
latest_tag = latest_release.get('tag_name', '')
# For pattern-matched releases, we need to extract the version part
# Example: "secrets-consul-v0.1.0" should become "0.1.0"
if github_release_pattern and latest_tag:
# Try to extract version from pattern like "secrets-consul-v0.1.0"
import re
# Look for version pattern after the prefix (v followed by semantic version)
version_match = re.search(r'v(\d+\.\d+\.\d+(?:-\d+)?)', latest_tag)
if version_match:
extracted_version = version_match.group(1)
latest_version, release_suffix = parse_github_version_release(extracted_version)
else:
# Fallback to full tag processing
latest_version, release_suffix = parse_github_version_release(latest_tag)
else:
latest_version, release_suffix = parse_github_version_release(latest_tag)
if not latest_version:
self.logger.warning(f"Could not determine latest version for {package_name}")
return False
@@ -1071,7 +1184,14 @@ class Builder:
if compare_versions(build.version, latest_version):
# Determine distro suffix based on repository configuration
distro_suffix = self._get_distro_suffix(build.repository)
new_release = f"1-{distro_suffix}" if distro_suffix else "1"
# Build the new release number
if release_suffix:
# Use extracted release suffix (e.g., "1" from "7.1.3-1")
new_release = f"{release_suffix}-{distro_suffix}" if distro_suffix else release_suffix
else:
# Default release numbering
new_release = f"1-{distro_suffix}" if distro_suffix else "1"
self.logger.info(f"New version available for {package_name}: {build.version} -> {latest_version}")
if not dry_run:
@@ -1104,6 +1224,10 @@ class Builder:
]
}
# Add github_release_pattern if it exists
if github_release_pattern:
updated_data['github_release_pattern'] = github_release_pattern
with open(metadata_file, 'w') as f:
yaml.dump(updated_data, f, default_flow_style=False, sort_keys=False)