From d34c72075d0bb7b9c500b8d4392d805e17285c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 18:18:44 +0100 Subject: [PATCH 01/11] Add version to trance & update schema --- mods/MathIsFun0@Trance/meta.json | 3 ++- schema/meta.schema.json | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mods/MathIsFun0@Trance/meta.json b/mods/MathIsFun0@Trance/meta.json index f58f6e6b..11af0d3f 100644 --- a/mods/MathIsFun0@Trance/meta.json +++ b/mods/MathIsFun0@Trance/meta.json @@ -6,5 +6,6 @@ "author": "MathIsFun0", "repo":"https://github.com/MathIsFun0/Trance", "downloadURL": "https://github.com/MathIsFun0/Trance/archive/refs/heads/main.zip", - "folderName": "Trance" + "folderName": "Trance", + "version": "1.0.0" } diff --git a/schema/meta.schema.json b/schema/meta.schema.json index 6586bae4..c5e95684 100644 --- a/schema/meta.schema.json +++ b/schema/meta.schema.json @@ -35,6 +35,10 @@ "folderName": { "type": "string", "pattern": "^[^<>:\"/\\|?*]+$" + }, + "version": { + "type": "string", + "pattern": "^[^<>:\"/\\|?*]+$" } }, "required": ["title", "requires-steamodded", "categories", "author", "repo", "downloadURL"] From 48bce017289c50ded2d14680e5dc56df26f2921b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 18:20:55 +0100 Subject: [PATCH 02/11] update version for testing --- mods/MathIsFun0@Trance/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/MathIsFun0@Trance/meta.json b/mods/MathIsFun0@Trance/meta.json index 11af0d3f..1bfd4b5d 100644 --- a/mods/MathIsFun0@Trance/meta.json +++ b/mods/MathIsFun0@Trance/meta.json @@ -7,5 +7,5 @@ "repo":"https://github.com/MathIsFun0/Trance", "downloadURL": "https://github.com/MathIsFun0/Trance/archive/refs/heads/main.zip", "folderName": "Trance", - "version": "1.0.0" + "version": "1.0.1" } From 784eac87539d2a0be6e9e7dfe20f7b74ecd5c18c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 18:24:26 +0100 Subject: [PATCH 03/11] Go back to original version --- mods/MathIsFun0@Trance/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/MathIsFun0@Trance/meta.json b/mods/MathIsFun0@Trance/meta.json index 1bfd4b5d..4a70afe4 100644 --- a/mods/MathIsFun0@Trance/meta.json +++ b/mods/MathIsFun0@Trance/meta.json @@ -7,5 +7,5 @@ "repo":"https://github.com/MathIsFun0/Trance", "downloadURL": "https://github.com/MathIsFun0/Trance/archive/refs/heads/main.zip", "folderName": "Trance", - "version": "1.0.1" + "version": "1.0.0", } From 15e16d5b389aad8f29f12016ba3ffed7dd4647ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 18:24:52 +0100 Subject: [PATCH 04/11] Fix JSOn --- mods/MathIsFun0@Trance/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/MathIsFun0@Trance/meta.json b/mods/MathIsFun0@Trance/meta.json index 4a70afe4..11af0d3f 100644 --- a/mods/MathIsFun0@Trance/meta.json +++ b/mods/MathIsFun0@Trance/meta.json @@ -7,5 +7,5 @@ "repo":"https://github.com/MathIsFun0/Trance", "downloadURL": "https://github.com/MathIsFun0/Trance/archive/refs/heads/main.zip", "folderName": "Trance", - "version": "1.0.0", + "version": "1.0.0" } From 73bf53990cd7b59eb3f208f92a3da096d4a4bc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 18:35:56 +0100 Subject: [PATCH 05/11] Remove version --- mods/MathIsFun0@Trance/meta.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mods/MathIsFun0@Trance/meta.json b/mods/MathIsFun0@Trance/meta.json index 11af0d3f..f58f6e6b 100644 --- a/mods/MathIsFun0@Trance/meta.json +++ b/mods/MathIsFun0@Trance/meta.json @@ -6,6 +6,5 @@ "author": "MathIsFun0", "repo":"https://github.com/MathIsFun0/Trance", "downloadURL": "https://github.com/MathIsFun0/Trance/archive/refs/heads/main.zip", - "folderName": "Trance", - "version": "1.0.0" + "folderName": "Trance" } From a7a4094aafa3625fd9db1aa33e00b1f7bdb8cd0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 18:41:24 +0100 Subject: [PATCH 06/11] Update README & remove pattern in schema --- README.md | 4 +++- schema/meta.schema.json | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d65c60a5..8a92a7cd 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,8 @@ This file stores essential metadata in JSON format. **Make sure you adhere to th "author": "Joe Mama", "repo": "https://github.com/joemama/extended-cards", "downloadURL": "https://github.com/joemama/extended-cards/releases/latest/extended-cards.tar.gz", - "folderName": "ExtendedCards" + "folderName": "ExtendedCards", + "version": "1.0.0" } ``` @@ -48,6 +49,7 @@ 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. ### 3. thumbnail.jpg (Optional) If included, this image will appear alongside your mod in the index. Maximum and recommended size is 1920x1080 pixels. diff --git a/schema/meta.schema.json b/schema/meta.schema.json index c5e95684..6f835217 100644 --- a/schema/meta.schema.json +++ b/schema/meta.schema.json @@ -37,8 +37,7 @@ "pattern": "^[^<>:\"/\\|?*]+$" }, "version": { - "type": "string", - "pattern": "^[^<>:\"/\\|?*]+$" + "type": "string" } }, "required": ["title", "requires-steamodded", "categories", "author", "repo", "downloadURL"] From f532231f52ee2aafaa52894fb865af094b8e9ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 19:36:28 +0100 Subject: [PATCH 07/11] Add automatic version updating feature --- .github/scripts/update_mod_versions.py | 181 ++++++++++++++++++++++ .github/workflows/update-mod-versions.yml | 52 +++++++ README.md | 3 +- schema/meta.schema.json | 3 + 4 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/update_mod_versions.py create mode 100644 .github/workflows/update-mod-versions.yml diff --git a/.github/scripts/update_mod_versions.py b/.github/scripts/update_mod_versions.py new file mode 100644 index 00000000..540e044f --- /dev/null +++ b/.github/scripts/update_mod_versions.py @@ -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) + diff --git a/.github/workflows/update-mod-versions.yml b/.github/workflows/update-mod-versions.yml new file mode 100644 index 00000000..edcb6dd0 --- /dev/null +++ b/.github/workflows/update-mod-versions.yml @@ -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 + diff --git a/README.md b/README.md index 8a92a7cd..95176a99 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/schema/meta.schema.json b/schema/meta.schema.json index 6f835217..0bfdd878 100644 --- a/schema/meta.schema.json +++ b/schema/meta.schema.json @@ -38,6 +38,9 @@ }, "version": { "type": "string" + }, + "automatic-version-check": { + "type": "boolean" } }, "required": ["title", "requires-steamodded", "categories", "author", "repo", "downloadURL"] From a3420f5d31d746a4e6ecfc269e5a41dc776c461b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 19:39:53 +0100 Subject: [PATCH 08/11] Make script executable --- .github/scripts/update_mod_versions.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/scripts/update_mod_versions.py diff --git a/.github/scripts/update_mod_versions.py b/.github/scripts/update_mod_versions.py old mode 100644 new mode 100755 From 0d38e05bf0d0c15b9860b08f87d69ac06aad813b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 19:41:19 +0100 Subject: [PATCH 09/11] Add automatic version checking for testing --- mods/WilsontheWolf@DebugPlus/meta.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mods/WilsontheWolf@DebugPlus/meta.json b/mods/WilsontheWolf@DebugPlus/meta.json index b57e0bd0..ce56c2a4 100644 --- a/mods/WilsontheWolf@DebugPlus/meta.json +++ b/mods/WilsontheWolf@DebugPlus/meta.json @@ -5,5 +5,6 @@ "categories": ["Technical"], "author": "WilsontheWolf", "repo":"https://github.com/WilsontheWolf/DebugPlus", - "downloadURL": "https://github.com/WilsontheWolf/DebugPlus/releases/latest/download/DebugPlus.zip" + "downloadURL": "https://github.com/WilsontheWolf/DebugPlus/releases/latest/download/DebugPlus.zip", + "automatic-version-check": true } From dfb79616486e5e3e9f334633e7222df76e54d94a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 19:43:10 +0100 Subject: [PATCH 10/11] Remove schedule for testing --- .github/workflows/update-mod-versions.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update-mod-versions.yml b/.github/workflows/update-mod-versions.yml index edcb6dd0..323161c6 100644 --- a/.github/workflows/update-mod-versions.yml +++ b/.github/workflows/update-mod-versions.yml @@ -1,9 +1,9 @@ name: Update Mod Versions on: - schedule: - - cron: '0 * * * *' # Run every hour - workflow_dispatch: # Allow manual triggers + # schedule: + # - cron: '0 * * * *' # Run every hour + workflow_dispatch: # Allow manual triggers # Remove the scheduled cron job during testing jobs: update-versions: From 810bce76500be2b9fd591326eead491b813136b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96=2E=20Efe=20D=2E?= <67526259+skyline69@users.noreply.github.com> Date: Sun, 9 Mar 2025 19:49:19 +0100 Subject: [PATCH 11/11] Enable schedule --- .github/workflows/update-mod-versions.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-mod-versions.yml b/.github/workflows/update-mod-versions.yml index 323161c6..72856eb3 100644 --- a/.github/workflows/update-mod-versions.yml +++ b/.github/workflows/update-mod-versions.yml @@ -1,8 +1,8 @@ name: Update Mod Versions on: - # schedule: - # - cron: '0 * * * *' # Run every hour + schedule: + - cron: '0 * * * *' # Run every hour workflow_dispatch: # Allow manual triggers # Remove the scheduled cron job during testing jobs: