feat: enhance GitHub release tracking with pattern support and version extraction
- 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:
+128
-4
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user