From 819a072b494cd2498226e2c8da9bc82447e15bd9 Mon Sep 17 00:00:00 2001 From: Loup-Garou911XD <90267658+Loup-Garou911XD@users.noreply.github.com> Date: Mon, 5 Jan 2026 03:00:15 +0530 Subject: [PATCH] autometa commit --- .github/workflows/ci.yml | 109 ++++++++------ .github/workflows/release.yml | 10 +- README.md | 90 ++++------- test/auto_apply_plugin_metadata.py | 140 ++++++++++++++++++ test/{get_changes.py => get_changelog.py} | 0 test/get_latest.py | 20 ++- test/test_checks.py | 3 +- ...ersion_is_lower.py => versioning_tools.py} | 17 ++- 8 files changed, 263 insertions(+), 126 deletions(-) create mode 100644 test/auto_apply_plugin_metadata.py rename test/{get_changes.py => get_changelog.py} (100%) rename test/{version_is_lower.py => versioning_tools.py} (61%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de36220e..37059338 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,15 @@ name: CI +# WORD OF CAUTION: +# TO anyone modifying this +# Things will break if you modify this +# without understanding how it works + +# A simple flow of this file: +# Apply AutoPEP8 → Apply Plugin Metadata → CRITICAL COMMIT (format + plugin meta) +# ← ← ← ← ← ↵ +# ↪ Apply Version Metadata → Commit (version meta) → Tests + on: push: branches: @@ -9,48 +19,59 @@ on: jobs: build: runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.12"] steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.head_ref }} - fetch-depth: 0 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Install Dependencies - run: | - python -m pip install -U pip - python -m pip install -U pycodestyle==2.12.1 autopep8 - python -m pip install -U -r test/pip_reqs.txt - - - name: Apply AutoPEP8 - run: | - autopep8 --in-place --recursive --max-line-length=100 . - - - name: Commit AutoPEP8 - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: "[ci] auto-format" - branch: ${{ github.head_ref }} - - - name: Apply Version Metadata - run: | - python test/auto_apply_version_metadata.py $(git log --pretty=format:'%h' -n 1) - - - name: Commit Version Metadata - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: "[ci] apply-version-metadata" - branch: ${{ github.head_ref }} - - - name: Execute Tests - run: | - python -m unittest discover -v + - uses: actions/checkout@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install Dependencies + run: | + python -m pip install -U pip + python -m pip install -U pycodestyle==2.12.1 autopep8 + python -m pip install -U -r test/pip_reqs.txt + + - name: Apply AutoPEP8 + run: | + autopep8 --in-place --recursive --max-line-length=100 . + + - name: Apply Plugin Metadata + if: github.event_name == 'pull_request_target' + env: + GH_TOKEN: ${{ github.token }} + run: | + CHANGED_FILES=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --jq '.[].filename') + python test/auto_apply_plugin_metadata.py "$CHANGED_FILES" + + # This is a CRITICAL COMMIT for the next step + # which bases this as the commit to get the sha to store in index.json or plugin.json + - name: Commit Plugin Metadata and AutoPEP8 + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "[ci] apply-plugin-metadata-and-formatting" + branch: ${{ github.head_ref }} + + - name: Apply Version Metadata + run: | + python test/auto_apply_version_metadata.py $(git log --pretty=format:'%h' -n 1) + + - name: Commit Version Metadata + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "[ci] apply-version-metadata" + branch: ${{ github.head_ref }} + + - name: Refresh Repository + run: | + git fetch origin + git reset --hard HEAD + + - name: Execute Tests + run: | + python -m unittest discover -v diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9cfbbd73..6db088c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,9 +12,9 @@ jobs: name: Create Release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.12' @@ -27,14 +27,14 @@ jobs: - name: set_variables run: | - output1=$(python3 test/get_latest.py get_latest_version) + output1=$(python3 test/get_latest.py get_latest_plugman_version) { echo "changelog<> "$GITHUB_OUTPUT" output2=$(python3 test/get_latest.py get_latest_api) - output3=$(python3 test/version_is_lower.py ${{ steps.previoustag.outputs.tag }}) + output3=$(python3 test/versioning_tools.py ${{ steps.previoustag.outputs.tag }}) echo "latestVersion=$output1" >> $GITHUB_OUTPUT echo "latestAPI=$output2" >> $GITHUB_OUTPUT echo "shouldRun=$output3" >> $GITHUB_OUTPUT diff --git a/README.md b/README.md index 47d81eaa..cda60d37 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,17 @@ Let's say you wanna submit this new utility-type plugin named as `sample_plugin. # ba_meta require api 9 import babase +plugman = dict( + plugin_name="sample_plugin", + description="A test plugin for demonstration purposes blah blah.", + external_url="https://www.youtube.com/watch?v=dQw4w9WgXcQ", + authors=[ + {"name": "Loup", "email": "loupg450@gmail.com", "discord": "loupgarou_"}, + {"name": "brostos", "email": "", "discord": "brostos"} + ], + version="1.0.0", +) + # ba_meta export babase.Plugin class Main(babase.Plugin): def on_app_running(self): @@ -107,49 +118,13 @@ class Main(babase.Plugin): ``` You'll have to fork this repository and add your `sample_plugin.py` plugin file into the appropriate directory, which for -utility plugin is [plugins/utilities](plugins/utilities). After that, you'll have to add an entry for your plugin -in [plugins/utilities.json](plugins/utilities.json) so that it gets picked up by the Plugin Manager in-game. - -To do this, you'll have to edit the file and add something like this: -```json -{ - "name": "Utilities", - ... - "plugins": { - ... - "sample_plugin": { - "description": "Shows screenmessages!", - "external_url": "", - "authors": [ - { - "name": "Alex", - "email": "alex@example.com", - "discord": null - } - ], - "versions": { - "1.0.0": null - } - }, - ... - } - ... -} -``` -You can add whatever you wanna add to these fields. However, leave the value for your version key as `null`: -```json -"1.0.0": null -``` -Version values will automatically be populated through github-actions (along with formatting your code as per PEP8 style +utility plugin is [plugins/utilities](plugins/utilities). After that, plugin details and version values will automatically be populated through github-actions in [plugins/utilities.json](plugins/utilities.json)(along with formatting your code as per PEP8 style guide) once you open a pull request. -Save `utilities.json` with your modified changes and now you can create a [pull request](../../compare) with the -plugin you've added and the modified JSON metadata file! - ### Updating a Plugin - Make a [pull request](../../compare) with whatever changes you'd like to make to an existing plugin, and add a new - version entry in your plugin category's JSON metadata file. + version number in your plugin in the plugman dict. #### Example @@ -160,39 +135,24 @@ diff --git a/plugins/utilities/sample_plugin.py b/plugins/utilities/sample_plugi index ebb7dcc..da2b312 100644 --- a/plugins/utilities/sample_plugin.py +++ b/plugins/utilities/sample_plugin.py -@@ -5,6 +5,7 @@ import babase - class Main(babase.Plugin): - def on_app_running(self): - babase.screenmessage("Hi! I am a sample plugin!") - - def has_settings_ui(self): +@@ -9,7 +9,7 @@ + {"name": "Loup", "email": "loupg450@gmail.com", "discord": "loupgarou_"}, + {"name": "brostos", "email": "", "discord": "brostos"} + ], +- version="1.0.0", ++ version="1.1.0", + ) + + # ba_meta export babase.Plugin +@@ -21,4 +21,4 @@ return True - + def show_settings_ui(self, source_widget): - babase.screenmessage("You tapped my settings!") + babase.screenmessage("Hey! This is my new screenmessage!") ``` -To name this new version as `1.1.0`, add `"1.1.0": null,` just above the previous plugin version in `utilities.json`: -```diff -diff --git a/plugins/utilities.json b/plugins/utilities.json -index d3fd5bc..34ce9ad 100644 ---- a/plugins/utilities.json -+++ b/plugins/utilities.json -@@ -14,7 +14,10 @@ - } - ], - "versions": { -- "1.0.0": null -+ "1.1.0": null, -+ "1.0.0": { -+ ... -+ } - } - }, - ... -``` -That's it! Now you can make a [pull request](../../compare) with both the updated `sample_plugin.py` and `utilities.json` files. +That's it! Now you can make a [pull request](../../compare) with the updated `sample_plugin.py` file. ## 3rd Party Plugin Sources diff --git a/test/auto_apply_plugin_metadata.py b/test/auto_apply_plugin_metadata.py new file mode 100644 index 00000000..ad2c24d8 --- /dev/null +++ b/test/auto_apply_plugin_metadata.py @@ -0,0 +1,140 @@ +import sys +import json +import ast +import os +import get_latest +import versioning_tools + +DEBUG = True + +print("DOES THIS RUN AUTO APPLY PLUGIN METADATA?") + + +def debug_print(*args, **kwargs): + if DEBUG: + try: + print(*args, **kwargs) + except Exception as e: + print(f"something went wrong while printing debug info : {e}") + + +def get_latest_version(plugin_name, category) -> str: + filepaths = { + "minigames": "plugins/minigames.json", + "utilities": "plugins/utilities.json", + "maps": "plugins/maps.json", + "plugman": "index.json", + } + + try: + if category != "plugman": + return get_latest.get_latest_plugin_version(plugin_name, filepaths[category]) + return get_latest.get_latest_plugman_version() + + except Exception as e: + raise e + + +def update_plugman_json(version): + with open("index.json", "r+") as file: + data = json.load(file) + plugman_version = int(get_latest_version("plugin_manager", "plugman").replace(".", "")) + current_version = int(version["version"].replace(".", "")) + + if current_version > plugman_version: + with open("index.json", "r+") as file: + data = json.load(file) + data[current_version] = None + data["versions"] = dict(sorted(data["versions"].items(), reverse=True)) + + +def update_plugin_json(plugin_info, category): + name = plugin_info["plugin_name"] + + with open(f"plugins/{category}.json", "r+") as file: + data = json.load(file) + try: + # Check if plugin is already in the json + plugin = data["plugins"][name] + plugman_version = int(versioning_tools.semantic_to_str( + get_latest_version(name, category))) + current_version = int(versioning_tools.semantic_to_str(plugin_info["version"])) + # Ensure the version is always greater from the already released version + if current_version > plugman_version: + plugin["versions"][plugin_info["version"]] = None + # Ensure latest version appears first + plugin["versions"] = dict(sorted(plugin["versions"].items(), reverse=True)) + plugin["description"] = plugin_info["description"] + plugin["external_url"] = plugin_info["external_url"] + plugin["authors"] = plugin_info["authors"] + elif current_version <= plugman_version: + raise Exception("Version cant be lower or equal than the previous version.") + except KeyError: + data["plugins"][name] = { + "description": plugin_info["description"], + "external_url": plugin_info["external_url"], + "authors": plugin_info["authors"], + "versions": {plugin_info["version"]: None}, + } + + file.seek(0) + json.dump(data, file, indent=2, ensure_ascii=False) + # Ensure old content is removed + file.truncate() + + +def extract_plugman(plugins): + for plugin in plugins: + if "plugins"+os.sep in plugin and plugin.endswith(".py"): + + debug_print(plugin) + try: + # Split the path and get the part after 'plugins/' + parts = plugin.split("plugins"+os.sep)[1].split(os.sep) + file_name_no_extension = plugin.split(os.sep)[-1].replace(".py", "") + category = parts[0] # First part after plugins/ + except ValueError: + if "plugin_manager" in plugin: + continue + with open(plugin, "r") as f: + tree = ast.parse(f.read()) + + for node in ast.walk(tree): + if isinstance(node, ast.Assign) and len(node.targets) == 1: + target = node.targets[0] + if isinstance(target, ast.Name) and target.id == "plugman": + if isinstance(node.value, ast.Dict): + # i dont want to support multiple formats for now + # because its harder to parse and maintain + # ill leave this here for now, though not supported + # Standard dictionary format {key: value} + return ast.literal_eval(node.value) + elif ( + isinstance(node.value, ast.Call) + and isinstance(node.value.func, ast.Name) + and node.value.func.id == "dict" + ): + # dict() constructor format + result = {} + for kw in node.value.keywords: + if kw.arg == "plugin_name": + plugin_name = ast.literal_eval(kw.value) + # some basic validation specific to plugin manager + if (plugin_name != plugin_name.lower()): + raise ValueError( + "Plugin name in plugman must be in snakecase.") + if (plugin_name != file_name_no_extension): + raise ValueError( + "Plugin name in plugman does not match the file name.") + result[kw.arg] = ast.literal_eval(kw.value) + if category: + update_plugin_json(result, category=category) + else: + update_plugman_json(result) + # raise ValueError("Variable plugman not found in the file or has unsupported format.") + + +if __name__ == "__main__": + plugins = sys.argv[1].split('\n') + debug_print(plugins) + extract_plugman(plugins) diff --git a/test/get_changes.py b/test/get_changelog.py similarity index 100% rename from test/get_changes.py rename to test/get_changelog.py diff --git a/test/get_latest.py b/test/get_latest.py index 3dde5db2..275cbbf4 100644 --- a/test/get_latest.py +++ b/test/get_latest.py @@ -2,7 +2,7 @@ import sys -def get_latest_version(): +def get_latest_plugman_version() -> str: """Get latest version entry from index.json""" with open('index.json', 'r') as file: content = json.loads(file.read()) @@ -10,16 +10,28 @@ def get_latest_version(): return latest_version +def get_latest_plugin_version(plugin_name, json_path) -> str: + """Get latest version entry from json file for a specific plugin""" + with open(json_path, 'r') as file: + content = json.loads(file.read()) + latest_version = list(content["plugins"][plugin_name]["versions"].keys())[0] + return latest_version + + def get_latest_api(): """Get latest api entry from index.json""" with open('index.json', 'r') as file: content = json.loads(file.read()) - latest_api = content["versions"][get_latest_version()]["api_version"] + latest_api = content["versions"][get_latest_plugman_version()]["api_version"] return latest_api if __name__ == "__main__": if len(sys.argv) < 2: - print(f"Usage: python3 {__file__.split('/')[-1]} function") + print(f"Usage: python3 {__file__.split('/')[-1]} function [args...]") sys.exit(1) - print(globals()[sys.argv[1]]()) # used to call the fucntion passed as cli parameter + function_name = sys.argv[1] + function_args = sys.argv[2:] + print( + globals()[function_name](*function_args) + ) # used to call the function passed as cli parameter with additional arguments diff --git a/test/test_checks.py b/test/test_checks.py index 69a6a4ee..c5bc6dfb 100644 --- a/test/test_checks.py +++ b/test/test_checks.py @@ -157,7 +157,7 @@ def test_versions(self): if md5sum != version_metadata["md5sum"]: self.fail( - f"{plugin} checksum changed;\n" + f"{plugin} checksum changed for version {version_name};\n" f"{version_metadata['md5sum']} (mentioned in {self.category_metadata_file}) ->\n" f"{md5sum} (actual)" ) @@ -177,6 +177,7 @@ def test_latest_version(self): if md5sum != latest_version_metadata["md5sum"]: self.fail( + f"Latest version {latest_version_name} of " f"{plugin} checksum changed;\n" f"{latest_version_metadata['md5sum']} (mentioned in {self.category_metadata_file}) ->\n" f"{md5sum} (actual)" diff --git a/test/version_is_lower.py b/test/versioning_tools.py similarity index 61% rename from test/version_is_lower.py rename to test/versioning_tools.py index 8b6f4293..6d75da9e 100644 --- a/test/version_is_lower.py +++ b/test/versioning_tools.py @@ -1,22 +1,25 @@ import sys -from get_latest import get_latest_version +from get_latest import get_latest_plugman_version + +"""if called directly from command line, it will check if given version is lower +than latest version in index.json""" def semantic_to_str(semantic_version: str): """Convert version in the form of v1.2.3 to 001002003 for comparing 2 version in semantic versioning format""" out = "" - for i in (semantic_version.split(".")): + for i in semantic_version.split("."): if len(i) == 1: - out += "00"+i + out += "00" + i if len(i) == 2: - out += "0"+i + out += "0" + i return out -def version_is_lower(version: str): +def plugman_version_is_lower_than(version: str): """Check if given version is lower than the latest entry in index.json""" - latest = semantic_to_str(get_latest_version()) + latest = semantic_to_str(get_latest_plugman_version()) version = semantic_to_str(version) if latest > version: return True @@ -30,5 +33,5 @@ def version_is_lower(version: str): sys.exit(1) version = sys.argv[1].replace("v", "", 1) - out = version_is_lower(version) + out = plugman_version_is_lower_than(version) print(int(out))