feat: add auto-update support for tags with no release assets
This commit is contained in:
196
.github/scripts/update_mod_versions.py
vendored
196
.github/scripts/update_mod_versions.py
vendored
@@ -3,12 +3,14 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import requests
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
# GitHub API rate limits are higher with authentication
|
# GitHub API rate limits are higher with authentication
|
||||||
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
|
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
|
||||||
HEADERS = {'Authorization': f'token {GITHUB_TOKEN}'} if GITHUB_TOKEN else {}
|
HEADERS = {'Authorization': f'token {GITHUB_TOKEN}'} if GITHUB_TOKEN else {}
|
||||||
@@ -24,12 +26,19 @@ def extract_repo_info(repo_url):
|
|||||||
return owner, repo
|
return owner, repo
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
def get_version_string(source, owner, repo, start_timestamp, n = 1):
|
VersionSource = Enum("VersionSource", [
|
||||||
|
("RELEASE_TAG", "release"),
|
||||||
|
("HEAD", "commit"),
|
||||||
|
])
|
||||||
|
def get_version_string(source: Enum, owner, repo, start_timestamp, n = 1):
|
||||||
"""Get the version string from a given GitHub repo."""
|
"""Get the version string from a given GitHub repo."""
|
||||||
|
|
||||||
if source == 'release':
|
if source is VersionSource.RELEASE_TAG:
|
||||||
url = f'https://api.github.com/repos/{owner}/{repo}/releases/latest'
|
url = f'https://api.github.com/repos/{owner}/{repo}/releases/latest'
|
||||||
else:
|
else:
|
||||||
|
if not source is VersionSource.HEAD:
|
||||||
|
print(f"UNIMPLEMENTED(VersionSource): `{source}`,\nfalling back to `HEAD`")
|
||||||
|
source = VersionSource.HEAD
|
||||||
url = f'https://api.github.com/repos/{owner}/{repo}/commits'
|
url = f'https://api.github.com/repos/{owner}/{repo}/commits'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -42,20 +51,30 @@ def get_version_string(source, owner, repo, start_timestamp, n = 1):
|
|||||||
api_resource = response.headers.get('x-ratelimit-resource')
|
api_resource = response.headers.get('x-ratelimit-resource')
|
||||||
print(f"GitHub API ({api_resource}) calls: {api_rate_usage}/{api_rate_limit}")
|
print(f"GitHub API ({api_resource}) calls: {api_rate_usage}/{api_rate_limit}")
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 404:
|
||||||
if source == 'release':
|
|
||||||
data = response.json()
|
|
||||||
# Return name of latest tag
|
|
||||||
return data.get('tag_name')
|
|
||||||
else:
|
|
||||||
commits = response.json()
|
|
||||||
if commits and len(commits) > 0:
|
|
||||||
# Return shortened commit hash (first 7 characters)
|
|
||||||
return commits[0]['sha'][:7]
|
|
||||||
elif response.status_code == 404:
|
|
||||||
# Not found
|
# Not found
|
||||||
return None
|
return None
|
||||||
elif api_rate_remaining == 0 or (response.status_code == 403 and 'rate limit exceeded' in response.text.lower()):
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if source is VersionSource.RELEASE_TAG:
|
||||||
|
# Return name of latest tag
|
||||||
|
return data.get('tag_name')
|
||||||
|
|
||||||
|
if data and len(data) > 0:
|
||||||
|
# Return shortened commit hash (first 7 characters)
|
||||||
|
return data[0]['sha'][:7]
|
||||||
|
|
||||||
|
print(f"⚠️ Warning: unexpected response format for {source}s:\n{
|
||||||
|
json.dumps(data, indent=2, ensure_ascii=False)
|
||||||
|
}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if api_rate_remaining == 0 or (
|
||||||
|
response.status_code == 403
|
||||||
|
and "rate limit exceeded" in response.text.lower()
|
||||||
|
):
|
||||||
print(f"GitHub API access is being rate limited!")
|
print(f"GitHub API access is being rate limited!")
|
||||||
current_timestamp = int(time.time())
|
current_timestamp = int(time.time())
|
||||||
|
|
||||||
@@ -95,86 +114,98 @@ def process_mods(start_timestamp):
|
|||||||
"""Process all mods and update versions where needed."""
|
"""Process all mods and update versions where needed."""
|
||||||
mods_dir = Path('mods')
|
mods_dir = Path('mods')
|
||||||
updated_mods = []
|
updated_mods = []
|
||||||
|
|
||||||
print(f"Scanning {mods_dir} for mods with automatic version control...")
|
print(f"Scanning {mods_dir} for mods with automatic version control...")
|
||||||
|
|
||||||
# Find all mod directories
|
# Find all mod directories
|
||||||
for mod_dir in [d for d in mods_dir.iterdir() if d.is_dir()]:
|
for mod_dir in (d for d in mods_dir.iterdir() if d.is_dir()):
|
||||||
meta_file = mod_dir / 'meta.json'
|
meta_file = mod_dir / 'meta.json'
|
||||||
|
|
||||||
if not meta_file.exists():
|
if not meta_file.exists():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
|
||||||
with open(meta_file, 'r', encoding='utf-8') as f:
|
|
||||||
meta = json.load(f)
|
|
||||||
|
|
||||||
# Skip mods without automatic version checking enabled
|
|
||||||
if not meta.get('automatic-version-check', False):
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f"Processing {mod_dir.name}...")
|
|
||||||
|
|
||||||
repo_url = meta.get('repo')
|
|
||||||
if not repo_url:
|
|
||||||
print(f"⚠️ Warning: Mod {mod_dir.name} has automatic-version-check but no repo URL")
|
|
||||||
continue
|
|
||||||
|
|
||||||
owner, repo = extract_repo_info(repo_url)
|
|
||||||
if not owner or not repo:
|
|
||||||
print(f"⚠️ Warning: Could not extract repo info from {repo_url}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f"Checking GitHub repo: {owner}/{repo}")
|
|
||||||
|
|
||||||
# If download url links to latest head, use version of latest commit hash
|
|
||||||
download_url = meta.get('downloadURL')
|
|
||||||
if "/archive/refs/heads/" in download_url:
|
|
||||||
print("Download URL links to HEAD, checking latest commit...")
|
|
||||||
version_source = "commit"
|
|
||||||
new_version = get_version_string(version_source, owner, repo, start_timestamp)
|
|
||||||
else:
|
|
||||||
# Try to get latest release version
|
|
||||||
print("Checking releases for latest version tag...")
|
|
||||||
version_source = "release"
|
|
||||||
new_version = get_version_string(version_source, owner, repo, start_timestamp)
|
|
||||||
|
|
||||||
# If no releases, fall back to latest commit
|
|
||||||
if not new_version:
|
|
||||||
print("No releases found, checking latest commit...")
|
|
||||||
version_source = "commit"
|
|
||||||
new_version = get_version_string(version_source, owner, repo, start_timestamp)
|
|
||||||
|
|
||||||
if not new_version:
|
try:
|
||||||
print(f"⚠️ Warning: Could not determine version for {mod_dir.name}")
|
if mod := process_mod(start_timestamp, mod_dir.name, mod_dir / 'meta.json'):
|
||||||
continue
|
updated_mods.append(mod)
|
||||||
|
|
||||||
current_version = meta.get('version')
|
|
||||||
|
|
||||||
# Update version if it changed
|
|
||||||
if current_version != new_version:
|
|
||||||
print(f"✅ Updating {mod_dir.name} from {current_version or 'none'} to {new_version} ({version_source})")
|
|
||||||
meta['version'] = new_version
|
|
||||||
|
|
||||||
with open(meta_file, 'w', encoding='utf-8') as f:
|
|
||||||
# Preserve formatting with indentation
|
|
||||||
json.dump(meta, f, indent=2, ensure_ascii=False)
|
|
||||||
f.write("\n") # Add newline at end of file
|
|
||||||
|
|
||||||
updated_mods.append({
|
|
||||||
'name': meta.get('title', mod_dir.name),
|
|
||||||
'old_version': current_version,
|
|
||||||
'new_version': new_version,
|
|
||||||
'source': version_source
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
print(f"ℹ️ No version change for {mod_dir.name} (current: {current_version})")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error processing {mod_dir.name}: {str(e)}")
|
print(f"❌ Error processing {mod_dir.name}: {str(e)}")
|
||||||
|
|
||||||
return updated_mods
|
return updated_mods
|
||||||
|
|
||||||
|
def process_mod(start_timestamp, name, meta_file):
|
||||||
|
if not meta_file.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(meta_file, 'r', encoding='utf-8') as f:
|
||||||
|
meta = json.load(f)
|
||||||
|
|
||||||
|
# Skip mods without automatic version checking enabled
|
||||||
|
if not meta.get('automatic-version-check'):
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Processing {name}...")
|
||||||
|
|
||||||
|
repo_url = meta.get('repo')
|
||||||
|
if not repo_url:
|
||||||
|
print(f"⚠️ Warning: Mod {name} has automatic-version-check but no repo URL")
|
||||||
|
return
|
||||||
|
|
||||||
|
owner, repo = extract_repo_info(repo_url)
|
||||||
|
if not owner or not repo:
|
||||||
|
print(f"⚠️ Warning: Could not extract repo info from {repo_url}")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Checking GitHub repo: `{owner}/{repo}`")
|
||||||
|
|
||||||
|
# If download url links to latest head, use version of latest commit hash
|
||||||
|
download_url = meta.get('downloadURL')
|
||||||
|
|
||||||
|
new_version = None
|
||||||
|
|
||||||
|
if "/archive/refs/heads/" in download_url:
|
||||||
|
print("Download URL links to HEAD, checking latest commit...")
|
||||||
|
source = VersionSource.HEAD
|
||||||
|
new_version = get_version_string(VersionSource.HEAD, owner, repo, start_timestamp)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Checking releases for latest version tag...")
|
||||||
|
source = VersionSource.RELEASE_TAG
|
||||||
|
new_version = get_version_string(source, owner, repo, start_timestamp)
|
||||||
|
|
||||||
|
if not new_version:
|
||||||
|
print("No releases found, falling back to latest commit instead...")
|
||||||
|
source = VersionSource.HEAD
|
||||||
|
new_version = get_version_string(source, owner, repo, start_timestamp)
|
||||||
|
|
||||||
|
if not new_version:
|
||||||
|
print(f"⚠️ Warning: Could not determine version for {name}")
|
||||||
|
return
|
||||||
|
|
||||||
|
current_version = meta.get('version')
|
||||||
|
# Update version if it changed
|
||||||
|
if current_version == new_version:
|
||||||
|
print(f"ℹ️ No version change for {name} (current: {current_version})")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"✅ Updating {name} from {current_version} to {new_version} ({source})"
|
||||||
|
)
|
||||||
|
meta['version'] = new_version
|
||||||
|
if "/archive/refs/tags/" in download_url:
|
||||||
|
meta['downloadURL'] = f"{repo_url}/archive/refs/tags/{meta['version']}.zip"
|
||||||
|
|
||||||
|
with open(meta_file, 'w', encoding='utf-8') as f:
|
||||||
|
# Preserve formatting with indentation
|
||||||
|
json.dump(meta, f, indent=2, ensure_ascii=False)
|
||||||
|
f.write("\n") # Add newline at end of file
|
||||||
|
|
||||||
|
return {
|
||||||
|
'name': meta.get('title', name),
|
||||||
|
'old_version': current_version,
|
||||||
|
'new_version': meta['version'],
|
||||||
|
'source': source
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def generate_commit_message(updated_mods):
|
def generate_commit_message(updated_mods):
|
||||||
"""Generate a detailed commit message listing all updated mods."""
|
"""Generate a detailed commit message listing all updated mods."""
|
||||||
if not updated_mods:
|
if not updated_mods:
|
||||||
@@ -191,6 +222,7 @@ def generate_commit_message(updated_mods):
|
|||||||
|
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
start_timestamp = int(time.time())
|
start_timestamp = int(time.time())
|
||||||
start_datetime = datetime.fromtimestamp(start_timestamp).strftime('%H:%M:%S')
|
start_datetime = datetime.fromtimestamp(start_timestamp).strftime('%H:%M:%S')
|
||||||
|
|||||||
Reference in New Issue
Block a user