From 5e2cfc3aa8be265fa32072fa38038d0099cc8893 Mon Sep 17 00:00:00 2001 From: Loup-Garou911XD <90267658+Loup-Garou911XD@users.noreply.github.com> Date: Mon, 8 Dec 2025 20:08:16 +0530 Subject: [PATCH 1/4] One clean commit after all the testing --- .github/workflows/ci.yml | 108 +++++++++++++---------- .github/workflows/release.yml | 6 +- README.md | 90 ++++++-------------- test/auto_apply_plugin_metadata.py | 132 +++++++++++++++++++++++++++++ test/get_latest.py | 20 ++++- test/test_checks.py | 3 +- 6 files changed, 242 insertions(+), 117 deletions(-) create mode 100644 test/auto_apply_plugin_metadata.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de36220..7fc19ef 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,58 @@ 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 + + - 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' + # should this only be run on pull_request_target and not pushes?? + run: | + LAST_COMMIT_HASH=$(git log --pretty=format:'%H' -n 1) + CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r "$LAST_COMMIT_HASH") + 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 9cfbbd7..59d8dd8 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,7 +27,7 @@ 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< 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(get_latest_version(name, category).replace(".", "")) + current_version = int(plugin_info["version"].replace(".", "")) + # 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/" in plugin: + + debug_print(plugin) + try: + # Split the path and get the part after 'plugins/' + parts = plugin.split("plugins/")[1].split("/") + file_name_no_extension = plugin.split("/")[-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_latest.py b/test/get_latest.py index 3dde5db..275cbbf 100644 --- a/test/get_latest.py +++ b/test/get_latest.py @@ -2,7 +2,7 @@ import json 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 69a6a4e..c5bc6df 100644 --- a/test/test_checks.py +++ b/test/test_checks.py @@ -157,7 +157,7 @@ class BaseCategoryMetadataTestCases: 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 @@ class BaseCategoryMetadataTestCases: 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)" From 9ff775e2f1a5475b40fcb1711385449685ec6fcd Mon Sep 17 00:00:00 2001 From: Loup-Garou911XD <90267658+Loup-Garou911XD@users.noreply.github.com> Date: Wed, 10 Dec 2025 19:38:03 +0530 Subject: [PATCH 2/4] cleanup --- .github/workflows/release.yml | 4 ++-- README.md | 2 +- test/auto_apply_plugin_metadata.py | 14 ++++++++------ test/{get_changes.py => get_changelog.py} | 0 test/{version_is_lower.py => versioning_tools.py} | 9 ++++++--- 5 files changed, 17 insertions(+), 12 deletions(-) rename test/{get_changes.py => get_changelog.py} (100%) rename test/{version_is_lower.py => versioning_tools.py} (79%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 59d8dd8..6db088c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,11 +30,11 @@ jobs: 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 6907fa7..cda60d3 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ 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, plugin getails 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 +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. ### Updating a Plugin diff --git a/test/auto_apply_plugin_metadata.py b/test/auto_apply_plugin_metadata.py index e694434..a88e48f 100644 --- a/test/auto_apply_plugin_metadata.py +++ b/test/auto_apply_plugin_metadata.py @@ -1,7 +1,9 @@ import sys import json import ast +import os import get_latest +import versioning_tools DEBUG = True @@ -11,7 +13,7 @@ def debug_print(*args, **kwargs): print(*args, **kwargs) -def get_latest_version(plugin_name, category): +def get_latest_version(plugin_name, category) -> str: filepaths = { "minigames": "plugins/minigames.json", "utilities": "plugins/utilities.json", @@ -49,8 +51,8 @@ def update_plugin_json(plugin_info, category): try: # Check if plugin is already in the json plugin = data["plugins"][name] - plugman_version = int(get_latest_version(name, category).replace(".", "")) - current_version = int(plugin_info["version"].replace(".", "")) + 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 @@ -77,13 +79,13 @@ def update_plugin_json(plugin_info, category): def extract_plugman(plugins): for plugin in plugins: - if "plugins/" in plugin: + 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/")[1].split("/") - file_name_no_extension = plugin.split("/")[-1].replace(".py", "") + 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: 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/version_is_lower.py b/test/versioning_tools.py similarity index 79% rename from test/version_is_lower.py rename to test/versioning_tools.py index 8b6f429..b2facfd 100644 --- a/test/version_is_lower.py +++ b/test/versioning_tools.py @@ -1,16 +1,19 @@ import sys from get_latest import get_latest_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 From 49a4c43830db79aee5ae1b69577adc6de45a1bcb Mon Sep 17 00:00:00 2001 From: Loup-Garou911XD <90267658+Loup-Garou911XD@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:08:42 +0000 Subject: [PATCH 3/4] [ci] apply-plugin-metadata-and-formatting --- test/auto_apply_plugin_metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/auto_apply_plugin_metadata.py b/test/auto_apply_plugin_metadata.py index a88e48f..9e94659 100644 --- a/test/auto_apply_plugin_metadata.py +++ b/test/auto_apply_plugin_metadata.py @@ -51,7 +51,8 @@ def update_plugin_json(plugin_info, category): 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))) + 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: From 0ba189615cc684803c08d210e73396e40c5425bb Mon Sep 17 00:00:00 2001 From: Loup-Garou911XD <90267658+Loup-Garou911XD@users.noreply.github.com> Date: Wed, 10 Dec 2025 21:09:38 +0530 Subject: [PATCH 4/4] fix --- .github/workflows/ci.yml | 11 ++++++----- test/auto_apply_plugin_metadata.py | 2 ++ test/versioning_tools.py | 8 ++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fc19ef..c3f3541 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,11 +2,11 @@ name: CI # WORD OF CAUTION: # TO anyone modifying this -# Things will break if you modify 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 AutoPEP8 → Apply Plugin Metadata → CRITICAL COMMIT (format + plugin meta) # ← ← ← ← ← ↵ # ↪ Apply Version Metadata → Commit (version meta) → Tests @@ -24,6 +24,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} - name: Set up Python uses: actions/setup-python@v6 @@ -42,10 +43,10 @@ jobs: - name: Apply Plugin Metadata if: github.event_name == 'pull_request_target' - # should this only be run on pull_request_target and not pushes?? + env: + GH_TOKEN: ${{ github.token }} run: | - LAST_COMMIT_HASH=$(git log --pretty=format:'%H' -n 1) - CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r "$LAST_COMMIT_HASH") + 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 diff --git a/test/auto_apply_plugin_metadata.py b/test/auto_apply_plugin_metadata.py index 9e94659..272ebc8 100644 --- a/test/auto_apply_plugin_metadata.py +++ b/test/auto_apply_plugin_metadata.py @@ -7,6 +7,8 @@ import versioning_tools DEBUG = True +print("DOES THIS RUN AUTO APPLY PLUGIN METADATA?") + def debug_print(*args, **kwargs): if DEBUG: diff --git a/test/versioning_tools.py b/test/versioning_tools.py index b2facfd..6d75da9 100644 --- a/test/versioning_tools.py +++ b/test/versioning_tools.py @@ -1,5 +1,5 @@ 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""" @@ -17,9 +17,9 @@ def semantic_to_str(semantic_version: str): 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 @@ -33,5 +33,5 @@ if __name__ == "__main__": 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))