Add automatic version updating feature
This commit is contained in:
181
.github/scripts/update_mod_versions.py
vendored
Normal file
181
.github/scripts/update_mod_versions.py
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# GitHub API rate limits are higher with authentication
|
||||
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
|
||||
HEADERS = {'Authorization': f'token {GITHUB_TOKEN}'} if GITHUB_TOKEN else {}
|
||||
|
||||
def extract_repo_info(repo_url):
|
||||
"""Extract owner and repo name from GitHub repo URL."""
|
||||
match = re.search(r'github\.com/([^/]+)/([^/]+)', repo_url)
|
||||
if match:
|
||||
owner = match.group(1)
|
||||
repo = match.group(2)
|
||||
# Remove .git suffix if present
|
||||
repo = repo.rstrip('.git')
|
||||
return owner, repo
|
||||
return None, None
|
||||
|
||||
def get_latest_release(owner, repo):
|
||||
"""Get the latest release version from GitHub."""
|
||||
url = f'https://api.github.com/repos/{owner}/{repo}/releases/latest'
|
||||
try:
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return data.get('tag_name')
|
||||
elif response.status_code == 404:
|
||||
# No releases found
|
||||
return None
|
||||
elif response.status_code == 403 and 'rate limit exceeded' in response.text.lower():
|
||||
print("GitHub API rate limit exceeded. Waiting for 5 minutes...")
|
||||
time.sleep(300) # Wait for 5 minutes
|
||||
return get_latest_release(owner, repo) # Retry
|
||||
else:
|
||||
print(f"Error fetching releases: HTTP {response.status_code} - {response.text}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Exception while fetching releases: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_latest_commit(owner, repo):
|
||||
"""Get the latest commit hash from GitHub."""
|
||||
url = f'https://api.github.com/repos/{owner}/{repo}/commits'
|
||||
try:
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
|
||||
if response.status_code == 200:
|
||||
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 == 403 and 'rate limit exceeded' in response.text.lower():
|
||||
print("GitHub API rate limit exceeded. Waiting for 5 minutes...")
|
||||
time.sleep(300) # Wait for 5 minutes
|
||||
return get_latest_commit(owner, repo) # Retry
|
||||
else:
|
||||
print(f"Error fetching commits: HTTP {response.status_code} - {response.text}")
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Exception while fetching commits: {str(e)}")
|
||||
return None
|
||||
|
||||
def process_mods():
|
||||
"""Process all mods and update versions where needed."""
|
||||
mods_dir = Path('mods')
|
||||
updated_mods = []
|
||||
|
||||
print(f"Scanning {mods_dir} for mods with automatic version control...")
|
||||
|
||||
# Find all mod directories
|
||||
for mod_dir in [d for d in mods_dir.iterdir() if d.is_dir()]:
|
||||
meta_file = mod_dir / 'meta.json'
|
||||
|
||||
if not meta_file.exists():
|
||||
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}")
|
||||
|
||||
# Try to get latest release version first
|
||||
new_version = get_latest_release(owner, repo)
|
||||
version_source = "release"
|
||||
|
||||
# If no releases, fall back to latest commit
|
||||
if not new_version:
|
||||
print("No releases found, checking latest commit...")
|
||||
new_version = get_latest_commit(owner, repo)
|
||||
version_source = "commit"
|
||||
|
||||
if not new_version:
|
||||
print(f"⚠️ Warning: Could not determine version for {mod_dir.name}")
|
||||
continue
|
||||
|
||||
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:
|
||||
print(f"❌ Error processing {mod_dir.name}: {str(e)}")
|
||||
|
||||
return updated_mods
|
||||
|
||||
def generate_commit_message(updated_mods):
|
||||
"""Generate a detailed commit message listing all updated mods."""
|
||||
if not updated_mods:
|
||||
return "No mod versions updated"
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
message = f"Auto-update mod versions ({timestamp})\n\n"
|
||||
message += "Updated mods:\n"
|
||||
|
||||
for mod in updated_mods:
|
||||
old_ver = mod['old_version'] or 'none'
|
||||
message += f"- {mod['name']}: {old_ver} → {mod['new_version']} ({mod['source']})\n"
|
||||
|
||||
return message
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"🔄 Starting automatic mod version update at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}...")
|
||||
updated_mods = process_mods()
|
||||
|
||||
if updated_mods:
|
||||
# Write commit message to a file that the GitHub Action can use
|
||||
commit_message = generate_commit_message(updated_mods)
|
||||
with open('commit_message.txt', 'w', encoding='utf-8') as f:
|
||||
f.write(commit_message)
|
||||
|
||||
print(f"✅ Completed. Updated {len(updated_mods)} mod versions.")
|
||||
else:
|
||||
print("ℹ️ Completed. No mod versions needed updating.")
|
||||
|
||||
# Exit with status code 0 even if no updates were made
|
||||
sys.exit(0)
|
||||
|
||||
52
.github/workflows/update-mod-versions.yml
vendored
Normal file
52
.github/workflows/update-mod-versions.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Update Mod Versions
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *' # Run every hour
|
||||
workflow_dispatch: # Allow manual triggers
|
||||
|
||||
jobs:
|
||||
update-versions:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # Needed to push changes
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
- name: Update mod versions
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
python .github/scripts/update_mod_versions.py
|
||||
|
||||
- name: Commit and push changes
|
||||
run: |
|
||||
git config --global user.name 'GitHub Actions Bot'
|
||||
git config --global user.email 'actions@github.com'
|
||||
|
||||
# Check if there are changes to commit
|
||||
if [[ $(git status --porcelain) ]]; then
|
||||
COMMIT_MSG="Auto-update mod versions"
|
||||
if [ -f commit_message.txt ]; then
|
||||
COMMIT_MSG=$(cat commit_message.txt)
|
||||
fi
|
||||
|
||||
git add mods/*/meta.json
|
||||
git commit -m "$COMMIT_MSG"
|
||||
git push
|
||||
else
|
||||
echo "No changes to commit"
|
||||
fi
|
||||
|
||||
@@ -49,7 +49,8 @@ This file stores essential metadata in JSON format. **Make sure you adhere to th
|
||||
- **repo**: A link to your mod's repository.
|
||||
- **downloadURL**: A direct link to the latest version of your released mod. (Can be same as `repo` if no separate download link exists.)
|
||||
- *folderName*: (*Optional*) The name for the mod's install folder. This must not contain characters `<` `>` `:` `"` `/` `\` `|` `?` `*`
|
||||
- *version*: (*Optional*, but **recommended**) The latest version of your mod.
|
||||
- *version*: (*Optional*, but **recommended**, if `automatic-version-check` disabled) The latest version of your mod.
|
||||
- *automatic-version-check*: (*Optional*, but **recommended**) Gets the latest release from your mod's repository and updates the `version` field. If there is no release, it will check the latest commit. Set this parameter to `true`, to enable this feature. (Note: the index updates every hour)
|
||||
|
||||
### 3. thumbnail.jpg (Optional)
|
||||
If included, this image will appear alongside your mod in the index. Maximum and recommended size is 1920x1080 pixels.
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"automatic-version-check": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["title", "requires-steamodded", "categories", "author", "repo", "downloadURL"]
|
||||
|
||||
Reference in New Issue
Block a user