Merge branch 'skyline69:main' into main
This commit is contained in:
5
.github/scripts/requirements.lock
vendored
Normal file
5
.github/scripts/requirements.lock
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
certifi==2025.1.31
|
||||
charset-normalizer==3.4.1
|
||||
idna==3.10
|
||||
requests==2.32.3
|
||||
urllib3==2.4.0
|
||||
196
.github/scripts/update_mod_versions.py
vendored
196
.github/scripts/update_mod_versions.py
vendored
@@ -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')
|
||||
|
||||
4
.github/workflows/update-mod-versions.yml
vendored
4
.github/workflows/update-mod-versions.yml
vendored
@@ -18,13 +18,13 @@ 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
|
||||
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:
|
||||
|
||||
175
.gitignore
vendored
175
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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.1.zip",
|
||||
"automatic-version-check": true,
|
||||
"version": "bce214b"
|
||||
"version": "1.6.1"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user