From 6ad5abd0fd61c3ed8c2bdf1fdda84e13d2ab09ef Mon Sep 17 00:00:00 2001 From: janw4ld Date: Sun, 20 Apr 2025 16:24:38 +0200 Subject: [PATCH 01/10] add standard python .gitignore --- .gitignore | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/.gitignore b/.gitignore index e43b0f98..9f7f4a8b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,176 @@ .DS_Store + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc From 5838831e49d1bf266682ded899f90f5c6fd42bdd Mon Sep 17 00:00:00 2001 From: janw4ld Date: Sun, 20 Apr 2025 16:26:24 +0200 Subject: [PATCH 02/10] nit(ci): lock python script dependencies --- .github/scripts/requirements.lock | 5 +++++ .github/workflows/update-mod-versions.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/requirements.lock diff --git a/.github/scripts/requirements.lock b/.github/scripts/requirements.lock new file mode 100644 index 00000000..9b59c223 --- /dev/null +++ b/.github/scripts/requirements.lock @@ -0,0 +1,5 @@ +certifi==2025.1.31 +charset-normalizer==3.4.1 +idna==3.10 +requests==2.32.3 +urllib3==2.4.0 diff --git a/.github/workflows/update-mod-versions.yml b/.github/workflows/update-mod-versions.yml index 7cb1ad74..48152822 100644 --- a/.github/workflows/update-mod-versions.yml +++ b/.github/workflows/update-mod-versions.yml @@ -24,7 +24,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r .github/scripts/requirements.txt + pip install -r .github/scripts/requirements.lock - name: Update mod versions env: From a66eb063a2dc1833cae0275ef7d433575d58d0ce Mon Sep 17 00:00:00 2001 From: janw4ld Date: Sun, 20 Apr 2025 17:37:17 +0200 Subject: [PATCH 03/10] feat: add auto-update support for tags with no release assets --- .github/scripts/update_mod_versions.py | 196 ++++++++++++++----------- 1 file changed, 114 insertions(+), 82 deletions(-) diff --git a/.github/scripts/update_mod_versions.py b/.github/scripts/update_mod_versions.py index b1426fdd..6b577d70 100755 --- a/.github/scripts/update_mod_versions.py +++ b/.github/scripts/update_mod_versions.py @@ -3,12 +3,14 @@ import json import os import re -import requests import sys import time from datetime import datetime +from enum import Enum from pathlib import Path +import requests + # 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 {} @@ -24,12 +26,19 @@ def extract_repo_info(repo_url): return owner, repo 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.""" - if source == 'release': + if source is VersionSource.RELEASE_TAG: url = f'https://api.github.com/repos/{owner}/{repo}/releases/latest' 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' try: @@ -42,20 +51,30 @@ def get_version_string(source, owner, repo, start_timestamp, n = 1): api_resource = response.headers.get('x-ratelimit-resource') print(f"GitHub API ({api_resource}) calls: {api_rate_usage}/{api_rate_limit}") - if response.status_code == 200: - 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: + if response.status_code == 404: # Not found 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!") current_timestamp = int(time.time()) @@ -95,86 +114,98 @@ def process_mods(start_timestamp): """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()]: + 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}") - - # 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: - 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})") - + try: + if mod := process_mod(start_timestamp, mod_dir.name, mod_dir / 'meta.json'): + updated_mods.append(mod) except Exception as e: print(f"❌ Error processing {mod_dir.name}: {str(e)}") 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): """Generate a detailed commit message listing all updated mods.""" if not updated_mods: @@ -191,6 +222,7 @@ def generate_commit_message(updated_mods): return message + if __name__ == "__main__": start_timestamp = int(time.time()) start_datetime = datetime.fromtimestamp(start_timestamp).strftime('%H:%M:%S') From b2437a0c571a284c9b4d34d76ea2ee16d27c0727 Mon Sep 17 00:00:00 2001 From: janw4ld Date: Sun, 20 Apr 2025 18:55:27 +0200 Subject: [PATCH 04/10] janw4ld@typist: switch `downloadURL` to release tag --- mods/janw4ld@typist/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/janw4ld@typist/meta.json b/mods/janw4ld@typist/meta.json index d8c870fb..ee7d7f8a 100644 --- a/mods/janw4ld@typist/meta.json +++ b/mods/janw4ld@typist/meta.json @@ -8,7 +8,7 @@ ], "author": "janw4ld", "repo": "https://github.com/janw4ld/balatro-typist-mod", - "downloadURL": "https://github.com/janw4ld/balatro-typist-mod/archive/refs/heads/main.zip", + "downloadURL": "https://github.com/janw4ld/balatro-typist-mod/archive/refs/tags/1.6.0.zip", "automatic-version-check": true, "version": "bce214b" } From be7b15673bc911c66518ff161279e5a4832f9d36 Mon Sep 17 00:00:00 2001 From: Version Update Bot Date: Mon, 21 Apr 2025 01:23:09 +0000 Subject: [PATCH 05/10] Auto-update mod versions (2025-04-21 01:23:08) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated mods: - Entropy: dfd7e40 → f18ba8e (commit) - Bakery: c52d911 → 5f89d04 (commit) --- mods/BakersDozenBagels@Bakery/meta.json | 2 +- mods/lordruby@Entropy/meta.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/BakersDozenBagels@Bakery/meta.json b/mods/BakersDozenBagels@Bakery/meta.json index 1b0c427a..bf360095 100644 --- a/mods/BakersDozenBagels@Bakery/meta.json +++ b/mods/BakersDozenBagels@Bakery/meta.json @@ -11,5 +11,5 @@ "repo": "https://github.com/BakersDozenBagels/BalatroBakery", "downloadURL": "https://github.com/BakersDozenBagels/BalatroBakery/archive/refs/heads/main.zip", "automatic-version-check": true, - "version": "c52d911" + "version": "5f89d04" } diff --git a/mods/lordruby@Entropy/meta.json b/mods/lordruby@Entropy/meta.json index b35b62cd..eb7c3069 100644 --- a/mods/lordruby@Entropy/meta.json +++ b/mods/lordruby@Entropy/meta.json @@ -9,6 +9,6 @@ "repo": "https://github.com/lord-ruby/Entropy", "downloadURL": "https://github.com/lord-ruby/Entropy/archive/refs/heads/main.zip", "folderName": "Entropy", - "version": "dfd7e40", + "version": "f18ba8e", "automatic-version-check": true } From 9817d835c493024bd79c576c72ccbde5f97f35c0 Mon Sep 17 00:00:00 2001 From: Version Update Bot Date: Mon, 21 Apr 2025 03:08:34 +0000 Subject: [PATCH 06/10] Auto-update mod versions (2025-04-21 03:08:34) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated mods: - JoyousSpring: 399d20b → 705074b (commit) --- mods/nh6574@JoyousSpring/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/nh6574@JoyousSpring/meta.json b/mods/nh6574@JoyousSpring/meta.json index 4a42fdb9..dc627fa7 100644 --- a/mods/nh6574@JoyousSpring/meta.json +++ b/mods/nh6574@JoyousSpring/meta.json @@ -10,5 +10,5 @@ "repo": "https://github.com/nh6574/JoyousSpring", "downloadURL": "https://github.com/nh6574/JoyousSpring/archive/refs/heads/master.zip", "automatic-version-check": true, - "version": "399d20b" + "version": "705074b" } From 7ef49ba036ba1f8a78595fbeee7e3e698b80b6b2 Mon Sep 17 00:00:00 2001 From: Version Update Bot Date: Mon, 21 Apr 2025 04:21:02 +0000 Subject: [PATCH 07/10] Auto-update mod versions (2025-04-21 04:21:01) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated mods: - Steamodded: 5858625 → 8654267 (commit) --- mods/Steamodded@smods/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/Steamodded@smods/meta.json b/mods/Steamodded@smods/meta.json index c16d0e52..5a69406b 100644 --- a/mods/Steamodded@smods/meta.json +++ b/mods/Steamodded@smods/meta.json @@ -9,5 +9,5 @@ "repo": "https://github.com/Steamodded/smods", "downloadURL": "https://github.com/Steamodded/smods/archive/refs/heads/main.zip", "automatic-version-check": true, - "version": "5858625" + "version": "8654267" } From 1cc98b275a7eda22df3a24f357afc597f0ae80c1 Mon Sep 17 00:00:00 2001 From: Version Update Bot Date: Mon, 21 Apr 2025 05:17:55 +0000 Subject: [PATCH 08/10] Auto-update mod versions (2025-04-21 05:17:55) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated mods: - Pokermon: 4595361 → fa172e8 (commit) - Maximus: 4a2da31 → 5d5afa7 (commit) --- mods/InertSteak@Pokermon/meta.json | 2 +- mods/theAstra@Maximus/meta.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/InertSteak@Pokermon/meta.json b/mods/InertSteak@Pokermon/meta.json index 4cb80f7c..98c5e40c 100644 --- a/mods/InertSteak@Pokermon/meta.json +++ b/mods/InertSteak@Pokermon/meta.json @@ -10,5 +10,5 @@ "repo": "https://github.com/InertSteak/Pokermon", "downloadURL": "https://github.com/InertSteak/Pokermon/archive/refs/heads/main.zip", "automatic-version-check": true, - "version": "4595361" + "version": "fa172e8" } diff --git a/mods/theAstra@Maximus/meta.json b/mods/theAstra@Maximus/meta.json index 7407ae22..41c76abf 100644 --- a/mods/theAstra@Maximus/meta.json +++ b/mods/theAstra@Maximus/meta.json @@ -9,5 +9,5 @@ "repo": "https://github.com/the-Astra/Maximus", "downloadURL": "https://github.com/the-Astra/Maximus/archive/refs/heads/main.zip", "automatic-version-check": true, - "version": "4a2da31" + "version": "5d5afa7" } From 83a157165046341d00550c176a5c48b92ec6e5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D9=88=D8=B1=D8=AF?= Date: Mon, 21 Apr 2025 09:24:18 +0200 Subject: [PATCH 09/10] fix(ci): switch to python3.12 for sensible f-strings https://peps.python.org/pep-0701/ :) --- .github/workflows/update-mod-versions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-mod-versions.yml b/.github/workflows/update-mod-versions.yml index 48152822..d70599ac 100644 --- a/.github/workflows/update-mod-versions.yml +++ b/.github/workflows/update-mod-versions.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.12' cache: 'pip' # This enables pip caching - name: Install dependencies From 52bcd8c7303a6c8a2a026a095914d6aeb8d62998 Mon Sep 17 00:00:00 2001 From: Version Update Bot Date: Mon, 21 Apr 2025 07:26:13 +0000 Subject: [PATCH 10/10] Auto-update mod versions (2025-04-21 07:26:13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated mods: - typist: bce214b → 1.6.1 (VersionSource.RELEASE_TAG) --- mods/janw4ld@typist/meta.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mods/janw4ld@typist/meta.json b/mods/janw4ld@typist/meta.json index ee7d7f8a..dc31eb1e 100644 --- a/mods/janw4ld@typist/meta.json +++ b/mods/janw4ld@typist/meta.json @@ -8,7 +8,7 @@ ], "author": "janw4ld", "repo": "https://github.com/janw4ld/balatro-typist-mod", - "downloadURL": "https://github.com/janw4ld/balatro-typist-mod/archive/refs/tags/1.6.0.zip", + "downloadURL": "https://github.com/janw4ld/balatro-typist-mod/archive/refs/tags/1.6.1.zip", "automatic-version-check": true, - "version": "bce214b" + "version": "1.6.1" }