From c81d3a2802cd3e973c994a54199e8f2b4411683a Mon Sep 17 00:00:00 2001 From: MaximumFX Date: Wed, 17 Apr 2024 13:16:34 +0200 Subject: [PATCH 1/3] Removed redundant print --- tk-readme-generator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tk-readme-generator.py b/tk-readme-generator.py index 9702fae..b56beb1 100644 --- a/tk-readme-generator.py +++ b/tk-readme-generator.py @@ -253,7 +253,6 @@ def sanitize(content: str): value, ) ) - print(cols, rows) readme += table(cols, rows) readme += "\n" From 9b81da43cb0ddd0e9ed132fbfcb2b714922c2b71 Mon Sep 17 00:00:00 2001 From: MaximumFX Date: Fri, 6 Sep 2024 11:53:02 +0200 Subject: [PATCH 2/3] Added readme generator for configurations --- .../update-config-readme.properties.json | 12 + ...te-readme.yml => update-config-readme.yml} | 1 + ... => update-general-readme.properties.json} | 0 .../update-general-readme.yml | 18 ++ README.md | 17 +- action.yml | 25 +- generator_config.py | 211 ++++++++++++ generator_general.py | 145 +++++++++ requirements.txt | 3 +- tk-readme-generator.py | 302 ++++-------------- utils.py | 196 ++++++++++++ 11 files changed, 683 insertions(+), 247 deletions(-) create mode 100644 .github/workflow-templates/update-config-readme.properties.json rename .github/workflow-templates/{update-readme.yml => update-config-readme.yml} (92%) rename .github/workflow-templates/{update-readme.properties.json => update-general-readme.properties.json} (100%) create mode 100644 .github/workflow-templates/update-general-readme.yml create mode 100644 generator_config.py create mode 100644 generator_general.py create mode 100644 utils.py diff --git a/.github/workflow-templates/update-config-readme.properties.json b/.github/workflow-templates/update-config-readme.properties.json new file mode 100644 index 0000000..74ab5ab --- /dev/null +++ b/.github/workflow-templates/update-config-readme.properties.json @@ -0,0 +1,12 @@ +{ + "name": "Update README.md", + "description": "CLI README.md generator for ShotGrid configurations.", + "iconName": "octicon book", + "categories": [ + "Automation", + "Python" + ], + "filePatterns": [ + "info.yml" + ] +} \ No newline at end of file diff --git a/.github/workflow-templates/update-readme.yml b/.github/workflow-templates/update-config-readme.yml similarity index 92% rename from .github/workflow-templates/update-readme.yml rename to .github/workflow-templates/update-config-readme.yml index 5bed4c8..72aa828 100644 --- a/.github/workflow-templates/update-readme.yml +++ b/.github/workflow-templates/update-config-readme.yml @@ -15,3 +15,4 @@ jobs: - uses: MaximumFX/tk-readme-generator@v1.0.4 with: github_token: ${{ secrets.GITHUB_TOKEN }} + mode: config diff --git a/.github/workflow-templates/update-readme.properties.json b/.github/workflow-templates/update-general-readme.properties.json similarity index 100% rename from .github/workflow-templates/update-readme.properties.json rename to .github/workflow-templates/update-general-readme.properties.json diff --git a/.github/workflow-templates/update-general-readme.yml b/.github/workflow-templates/update-general-readme.yml new file mode 100644 index 0000000..ea266e9 --- /dev/null +++ b/.github/workflow-templates/update-general-readme.yml @@ -0,0 +1,18 @@ +name: Update README.md + +on: + push: + paths: + - "info.yml" + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: MaximumFX/tk-readme-generator@v1.0.4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + mode: general diff --git a/README.md b/README.md index 2b13d42..1269961 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,17 @@ # ShotGrid Readme Generator -CLI/GitHub Action README.md generator for ShotGrid frameworks/engines/apps. +CLI/GitHub Action README.md generator for ShotGrid frameworks/engines/apps and configurations. ## GitHub Action Add this GitHub Action to a workflow to automatically update the README.md when a change is committed to info.yml. -See an example workflow [here](.github/workflow-templates/update-readme.yml). +See example workflows: [framework/engine/app](.github/workflow-templates/update-general-readme.yml) and [configuration](.github/workflow-templates/update-config-readme.yml). ## CLI ### Requirements -Requires `pyyaml` +Requires `pyyaml`, `semver` ### Usage @@ -21,8 +21,9 @@ Requires `pyyaml` #### Options -| Argument | Description | -|---------------------------------|-----------------------------------------------------------------| -| -o, --override | Override the existing readme. | -| -p FILEPATH, --prepend FILEPATH | Prepend an existing readme file after the name and description. | -| -a FILEPATH, --append FILEPATH | Append an existing readme file to the end. | +| Argument | Description | +|---------------------------------|------------------------------------------------------------------------------------------------------------------------| +| -o, --override | Override the existing readme. | +| -p FILEPATH, --prepend FILEPATH | Prepend an existing readme file after the name and description. | +| -a FILEPATH, --append FILEPATH | Append an existing readme file to the end. | +| -m MODE, --mode MODE | Make a readme for a _general_ info file (framework/engine/app) or a _config_ info file. (Options: `general`, `config`) | diff --git a/action.yml b/action.yml index 6529e1c..724012b 100644 --- a/action.yml +++ b/action.yml @@ -18,29 +18,44 @@ inputs: append: required: false description: Append an existing readme file to the end. + mode: + required: false + description: > + Make a readme for a general info file (framework/engine/app) or a config info file. + If none provided the type will be try to be detected automatically. + type: choice + options: + - general + - config runs: using: composite steps: - - name: checkout repo content + - name: Checkout repo content uses: actions/checkout@v4 - - name: setup python + - name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.9' - - name: install python packages + - name: Install Python packages shell: bash run: | python -m pip install --upgrade pip pip install -r ${{ github.action_path }}/requirements.txt - - name: execute py script + - name: Execute py script with automatic mode shell: bash + if: "${{ inputs.mode == '' }}" run: python ${{ github.action_path }}/tk-readme-generator.py "${{ inputs.infoFile }}" -o -p "${{ inputs.prepend }}" -a "${{ inputs.append }}" - - name: commit files + - name: Execute py script with mode + shell: bash + if: "${{ inputs.mode != '' }}" + run: python ${{ github.action_path }}/tk-readme-generator.py "${{ inputs.infoFile }}" -o -p "${{ inputs.prepend }}" -a "${{ inputs.append }}" -m ${{ inputs.mode }} + + - name: Commit files shell: bash run: | git config --local user.email "action@github.com" diff --git a/generator_config.py b/generator_config.py new file mode 100644 index 0000000..5d9a43e --- /dev/null +++ b/generator_config.py @@ -0,0 +1,211 @@ +""" +Readme generator for ShotGrid configurations +""" + +import subprocess +from pathlib import Path + +import semver +import yaml + +import utils + + +def _get_include(base_filepath: Path, filename: str, info_key: str) -> str: + """ + Get the information for an env/include file. + + Args: + base_filepath: Filepath to info.yml + filename: Filename to look for + info_key: Key to use for info in file + + Returns: + Include section with version status table + """ + filepath = base_filepath.parent / "env" / "includes" / f"{filename}.yml" + + if not filepath.is_file(): + return "" + + with open(str(filepath), "r") as file: + info = yaml.safe_load(file) + + if info is None: + return "" + + info = utils.yaml_to_nested_dict(info)[info_key] + + items = [] + rows = [] + + for key, value in info.items(): + item = {**value["location"], "key": key} + + if item["type"] == "app_store": + item["repo"] = f"ShotGunSoftware/{item['name']}" + elif item["type"] == "github_release": + item["repo"] = f"{item['organization']}/{item['repository']}" + else: + continue + + print( + f"[{utils.capitalize(info_key)}] Getting latest version for {item['repo']}..." + ) + + repo_url = f"https://github.com/{item['repo']}.git" + output_lines = subprocess.check_output( + [ + "git", + "ls-remote", + "--tags", + "--refs", + "--sort=version:refname", + repo_url, + ], + encoding="utf-8", + ).splitlines() + latest_version = output_lines[-1].rpartition("/")[-1] + + source_version = None + + if item["type"] == "github_release": + try: + source_lines = subprocess.check_output( + [ + "git", + "ls-remote", + "--tags", + "--refs", + "--sort=version:refname", + f"https://github.com/ShotGunSoftware/{item['repository']}.git", + ], + encoding="utf-8", + ).splitlines() + source_version = source_lines[-1].rpartition("/")[-1] + except: + pass + + item["latest"] = latest_version + if source_version is not None: + compare_version = source_version + else: + compare_version = latest_version + version = item["version"] + + if version.startswith("v"): + version = version[1:] + if compare_version.startswith("v"): + compare_version = compare_version[1:] + + version_count = version.count(".") + compare_count = compare_version.count(".") + if version_count != compare_count: + if version_count > 2: + parts = version.split(".") + version = ".".join(parts[0:3]) + + if compare_count > 2: + parts = compare_version.split(".") + compare_version = ".".join(parts[0:3]) + else: + if version_count > 2: + parts = version.split(".") + version = ".".join(parts[-3:]) + + parts = compare_version.split(".") + compare_version = ".".join(parts[-3:]) + + try: + item["compared"] = semver.compare(version, compare_version) + except: + item["compared"] = None + + items.append(item) + + color = "blue" + if item["compared"] == -1: + if source_version is None: + color = "red" + else: + color = "orange" + if item["compared"] == 1: + color = "green" + + if source_version is None: + rows.append( + [ + item["key"], + f"![{item['version']}](https://img.shields.io/badge/{item['version']}-{color})", + "", + f"[![{item['repo']}](https://img.shields.io/github/v/tag/{item['repo']})](https://github.com/{item['repo']})", + ] + ) + else: + rows.append( + [ + item["key"], + f"![{item['version']}](https://img.shields.io/badge/{item['version']}-{color})", + f"[![{item['repo']}](https://img.shields.io/github/v/tag/{item['repo']})](https://github.com/{item['repo']})", + f"[![ShotGunSoftware/{item['repository']}](https://img.shields.io/github/v/tag/ShotGunSoftware/{item['repository']})](https://github.com/ShotGunSoftware/{item['repository']})", + ] + ) + + title = utils.capitalize(info_key) + + return f"## {title}\n\n" + utils.make_table( + ["Name", "Current", "Fork", "Latest"], rows + ) + + +def generate_config_readme( + filepath: Path, prepend: str = None, append: str = None +) -> str: + """ + Generate a readme file for a ShotGrid framework/engine/app + + Args: + filepath: File path to info.yml + prepend: File path to Markdown file to prepend in Readme + append: File path to Markdown file to append in Readme + + Returns: + Markdown readme + """ + with open(str(filepath), "r") as file: + info = yaml.safe_load(file) + + readme = "" + + readme += utils.get_header(info, filepath) + + # Prepend readme file + if prepend is not None and prepend != "": + prepend_filepath = Path(prepend) + if prepend_filepath.is_file(): + with open(prepend_filepath, "r") as prepend_file: + readme += prepend_file.read() + readme += "\n\n" + + readme += "## Requirements\n\n" + + readme += utils.get_general_requirements(info) + + print("Getting framework info...") + readme += _get_include(filepath, "frameworks", "frameworks") + readme += "\n\n" + print("Getting engine info...") + readme += _get_include(filepath, "engine_locations", "engines") + readme += "\n\n" + print("Getting app info...") + readme += _get_include(filepath, "app_locations", "apps") + + # Append readme file + if append is not None and append != "": + append_filepath = Path(append) + if append_filepath.is_file(): + with open(append_filepath, "r") as append_file: + readme += "\n\n" + readme += append_file.read() + + return readme diff --git a/generator_general.py b/generator_general.py new file mode 100644 index 0000000..3d1d53e --- /dev/null +++ b/generator_general.py @@ -0,0 +1,145 @@ +""" +Readme generator for ShotGrid frameworks/engines/apps +""" + +from pathlib import Path + +import yaml + +import utils + + +def generate_general_readme( + filepath: Path, prepend: str = None, append: str = None +) -> str: + """ + Generate a readme file for a ShotGrid framework/engine/app + + Args: + filepath: File path to info.yml + prepend: File path to Markdown file to prepend in Readme + append: File path to Markdown file to append in Readme + + Returns: + Markdown readme + """ + with open(str(filepath), "r") as file: + info = yaml.safe_load(file) + + readme = "" + + readme += utils.get_header(info, filepath) + + # Prepend readme file + if prepend is not None and prepend != "": + prepend_filepath = Path(prepend) + if prepend_filepath.is_file(): + with open(prepend_filepath, "r") as prepend_file: + readme += prepend_file.read() + readme += "\n\n" + + readme += "## Requirements\n\n" + + readme += utils.get_general_requirements(info) + + if ( + "requires_shotgun_fields" in info + and info["requires_shotgun_fields"] is not None + ): + readme += "### ShotGrid fields:\n\n" + for key, value in info["requires_shotgun_fields"].items(): + readme += f"**{key}:**\n" + for field in value: + readme += f"- {field['system_name']} `{field['type']}`\n" + readme += "\n" + + if "frameworks" in info and info["frameworks"] is not None: + readme += "**Frameworks:**\n\n" + readme += utils.make_table( + ["Name", "Version", "Minimum version"], + list( + map( + lambda framework: [ + framework["name"], + framework["version"], + framework.get("minimum_version", ""), + ], + info["frameworks"], + ) + ), + ) + readme += "\n\n" + + config_names = { + "str": "Strings", + "int": "Integers", + "bool": "Booleans", + "dict": "Dictionaries", + "list": "Lists", + "config_path": "Config paths", + "template": "Templates", + "publish_type": "Publish types", + "hook": "Hooks", + "shotgun_entity_type": "ShotGrid entity types", + "shotgun_permission_group": "ShotGrid permission groups", + "shotgun_filter": "ShotGrid filters", + "tank_type": "Tank types", + } + if "configuration" in info and info["configuration"] is not None: + readme += "## Configuration\n\n" + + config = {} + for key, value in info["configuration"].items(): + config_item = {**value, "name": key} + + if "default_value" not in config_item: + config_item["default_value"] = "" + + if value["type"] not in config: + config[value["type"]] = [config_item] + else: + config[value["type"]] += [config_item] + + for key, value in config.items(): + name = key + if key in config_names: + name = config_names[key] + readme += f"### {name}\n\n" + + cols = ["Name", "Description", "Default value"] + if key == "template": + cols.append("Fields") + rows = list( + map( + lambda item: [ + f"`{item['name']}`", + item.get("description", ""), + item.get("default_value", ""), + item.get("fields", ""), + ], + value, + ) + ) + else: + rows = list( + map( + lambda item: [ + f"`{item['name']}`", + item.get("description", ""), + f'{item.get("default_value", "")}', + ], + value, + ) + ) + readme += utils.make_table(cols, rows) + readme += "\n" + + # Append readme file + if append is not None and append != "": + append_filepath = Path(append) + if append_filepath.is_file(): + with open(append_filepath, "r") as append_file: + readme += "\n\n" + readme += append_file.read() + + return readme diff --git a/requirements.txt b/requirements.txt index 18e7310..7c903fc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -PyYAML~=6.0.1 \ No newline at end of file +PyYAML~=6.0.1 +semver~=3.0.2 \ No newline at end of file diff --git a/tk-readme-generator.py b/tk-readme-generator.py index b56beb1..620e2e7 100644 --- a/tk-readme-generator.py +++ b/tk-readme-generator.py @@ -1,37 +1,23 @@ +""" +Readme generator for ShotGrid configurations, apps, frameworks and engines. +""" + import argparse +from enum import Enum from pathlib import Path -import yaml - -def table(cols: list[str], rows: list[list[str]]) -> str: - def sanitize(content: str): - if content is None: - return "" - return content.strip().replace("\n", "
").replace("|", "\\|") +import yaml - sizes = list(map(len, cols)) - for row in rows: - for i, item in enumerate(row): - item = sanitize(item) - if len(item) > sizes[i]: - sizes[i] = len(item) +from generator_config import generate_config_readme +from generator_general import generate_general_readme - table_md = "" - for i, col in enumerate(cols): - content = sanitize(col) - table_md += f"| {content}{' ' * (sizes[i] - len(content) + 1)}" - table_md += "|\n" - for i, col in enumerate(cols): - table_md += f"|{'-' * (sizes[i] + 2)}" - table_md += "|\n" +class Mode(Enum): + general = "general" + config = "config" - for row in rows: - for i, item in enumerate(row): - content = sanitize(item) - table_md += f"| {content}{' ' * (sizes[i] - len(content) + 1)}" - table_md += "|\n" - return table_md + "\n" + def __str__(self): + return self.value parser = argparse.ArgumentParser( @@ -55,219 +41,69 @@ def sanitize(content: str): metavar="FILEPATH", help="Append an existing readme file to the end.", ) - -args = parser.parse_args() - - -filepath = Path(args.file) -if not filepath.is_file(): - msg = "The file couldn't be found." - raise Exception(msg) - -with open(args.file, "r") as file: - info = yaml.safe_load(file) - -if info is None: - msg = "The file couldn't be parsed." - raise Exception(msg) - -readme = "" - -# GitHub shields -git_file = Path(filepath.parent) / ".git" / "config" -if git_file.is_file(): - with open(git_file, "r") as git_config_file: - git_config = git_config_file.read() - if '[remote "origin"]' in git_config: - lines = git_config.split("\n") - url = ( - lines[lines.index('[remote "origin"]') + 1] - .replace("url = ", "") - .replace(".git", "") - .strip() - ) - if "github" in url: - url_segments = url.split("/") - repo = f"{url_segments[-2]}/{url_segments[-1]}" - readme += f"[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/{repo}?include_prereleases)]({url}) \n" - readme += f"[![GitHub issues](https://img.shields.io/github/issues/{repo})]({url}/issues) \n" - -# Python project shields -pyproject = Path(filepath.parent) / "pyproject.toml" -if pyproject.is_file(): - with open(pyproject, "r") as pyproject_file: - pyproject_content = pyproject_file.read() - if ( - 'profile = "black"' in pyproject_content - or "[tool.black]" in pyproject_content - ): - readme += "[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n" - -if readme != "": - readme += "\n\n" - -if "display_name" in info: - readme += f"# {info['display_name']}" -else: - readme = f"# {Path(filepath.parent).name}" - -if (Path(filepath.parent) / "icon_256.png").is_file(): - readme += ' Icon' - -readme += "\n\n" - -if "documentation_url" in info: - readme += f"[![Documentation](https://img.shields.io/badge/documentation-blue?style=for-the-badge)]({info['documentation_url']})\n" -if "support_url" in info: - readme += f"[![Support](https://img.shields.io/badge/support-orange?style=for-the-badge)]({info['support_url']})\n" -if "documentation_url" in info or "support_url" in info: - readme += "\n" - -if "description" in info: - readme += f"{info['description']}\n\n" - -if "supported_engines" in info and info["supported_engines"] is not None: - readme += f"> Supported engines: {', '.join(info['supported_engines'])}\n\n" - -# Prepend readme file -if args.prepend is not None and args.prepend != "": - prepend_filepath = Path(args.prepend) - if prepend_filepath.is_file(): - with open(prepend_filepath, "r") as prepend_file: - readme += prepend_file.read() - readme += "\n\n" - -readme += "## Requirements\n\n" - -requires_shotgun_version = "-" -if "requires_shotgun_version" in info and info["requires_shotgun_version"] is not None: - requires_shotgun_version = info["requires_shotgun_version"] - -requires_core_version = "-" -if "requires_core_version" in info and info["requires_core_version"] is not None: - requires_core_version = info["requires_core_version"] - -requires_engine_version = "-" -if "requires_engine_version" in info and info["requires_engine_version"] is not None: - requires_engine_version = info["requires_engine_version"] - -readme += table( - [ - "ShotGrid version", - "Core version", - "Engine version", - ], - [ - [ - requires_shotgun_version, - requires_core_version, - requires_engine_version, - ] - ], +parser.add_argument( + "-m", + "--mode", + type=Mode, + choices=list(Mode), + help="Make a readme for a general info file (framework/engine/app) or a config info file.", ) -if "requires_shotgun_fields" in info and info["requires_shotgun_fields"] is not None: - readme += "### ShotGrid fields:\n\n" - for key, value in info["requires_shotgun_fields"].items(): - readme += f"**{key}:**\n" - for field in value: - readme += f"- {field['system_name']} `{field['type']}`\n" - readme += "\n" - -if "frameworks" in info and info["frameworks"] is not None: - readme += "**Frameworks:**\n\n" - readme += table( - ["Name", "Version", "Minimum version"], - list( - map( - lambda framework: [ - framework["name"], - framework["version"], - framework.get("minimum_version", ""), - ], - info["frameworks"], - ) - ), - ) - readme += "\n\n" - -config_names = { - "str": "Strings", - "int": "Integers", - "bool": "Booleans", - "dict": "Dictionaries", - "list": "Lists", - "config_path": "Config paths", - "template": "Templates", - "publish_type": "Publish types", - "hook": "Hooks", - "shotgun_entity_type": "ShotGrid entity types", - "shotgun_permission_group": "ShotGrid permission groups", - "shotgun_filter": "ShotGrid filters", - "tank_type": "Tank types", -} -if "configuration" in info and info["configuration"] is not None: - readme += "## Configuration\n\n" - - config = {} - configuration = info["configuration"] - for key, value in info["configuration"].items(): - config_item = {**value, "name": key} +args = parser.parse_args() - if "default_value" not in config_item: - config_item["default_value"] = "" - if value["type"] not in config: - config[value["type"]] = [config_item] +def generate_readme( + file: str, + mode: Mode = None, + override: bool = False, + prepend: str = None, + append: str = None, +): + """ + Generate a readme from an info.yml file. + + Args: + file: File path to info.yml + mode: Readme type to create + override: Override existing README.md (Default is `False`) + prepend: File path to Markdown file to prepend in Readme + append: File path to Markdown file to append in Readme + """ + + filepath = Path(file) + if not filepath.is_file(): + msg = "The file couldn't be found." + raise Exception(msg) + + with open(file, "r") as file: + info = yaml.safe_load(file) + + if info is None: + msg = "The file couldn't be parsed." + raise Exception(msg) + + # Decide mode if not provided + if mode is None: + filepath = filepath.parent / "env" / "includes" + if filepath.is_dir(): + mode = Mode.config else: - config[value["type"]] += [config_item] + mode = Mode.general - for key, value in config.items(): - name = key - if key in config_names: - name = config_names[key] - readme += f"### {name}\n\n" - - cols = ["Name", "Description", "Default value"] - if key == "template": - cols.append("Fields") - rows = list( - map( - lambda item: [ - f"`{item['name']}`", - item.get("description", ""), - item.get("default_value", ""), - item.get("fields", ""), - ], - value, - ) - ) - else: - rows = list( - map( - lambda item: [ - f"`{item['name']}`", - item.get("description", ""), - f'{item.get("default_value", "")}', - ], - value, - ) - ) - readme += table(cols, rows) - readme += "\n" + # Generate readme + if mode == Mode.general: + readme = generate_general_readme(filepath, prepend, append) + else: + readme = generate_config_readme( + filepath, + ) -# Append readme file -if args.append is not None and args.append != "": - append_filepath = Path(args.append) - if append_filepath.is_file(): - with open(append_filepath, "r") as append_file: - readme += "\n\n" - readme += append_file.read() + output_file = Path(filepath.parent) / "README.md" + if output_file.is_file() and not override: + output_file = Path(filepath.parent) / "README_GENERATED.md" + with open(output_file, "w") as text_file: + text_file.write(readme) -output_file = Path(filepath.parent) / "README.md" -if output_file.is_file() and not args.override: - output_file = Path(filepath.parent) / "README_GENERATED.md" -with open(output_file, "w") as text_file: - text_file.write(readme) +generate_readme(args.file, args.mode, args.override, args.prepend, args.append) diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..e17494f --- /dev/null +++ b/utils.py @@ -0,0 +1,196 @@ +""" +Utilities for tk-readme-generator +""" + +from pathlib import Path + + +def get_header(info: dict, filepath: Path) -> str: + """ + Get the header of the readme file with shields. + + Args: + info: Info data + filepath: Info filepath + + Returns: + Readme header + """ + readme = "" + + # GitHub shields + git_file = Path(filepath.parent) / ".git" / "config" + if git_file.is_file(): + with open(git_file, "r") as git_config_file: + git_config = git_config_file.read() + if '[remote "origin"]' in git_config: + lines = git_config.split("\n") + url = ( + lines[lines.index('[remote "origin"]') + 1] + .replace("url = ", "") + .replace(".git", "") + .strip() + ) + if "github" in url: + url_segments = url.split("/") + repo = f"{url_segments[-2]}/{url_segments[-1]}" + readme += f"[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/{repo}?include_prereleases)]({url}) \n" + readme += f"[![GitHub issues](https://img.shields.io/github/issues/{repo})]({url}/issues) \n" + + # Python project shields + pyproject = Path(filepath.parent) / "pyproject.toml" + if pyproject.is_file(): + with open(pyproject, "r") as pyproject_file: + pyproject_content = pyproject_file.read() + if ( + 'profile = "black"' in pyproject_content + or "[tool.black]" in pyproject_content + ): + readme += "[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n" + + if readme != "": + readme += "\n\n" + + if "display_name" in info: + readme += f"# {info['display_name']}" + else: + readme = f"# {Path(filepath.parent).name}" + + if (Path(filepath.parent) / "icon_256.png").is_file(): + readme += ' Icon' + + readme += "\n\n" + + if "documentation_url" in info: + readme += f"[![Documentation](https://img.shields.io/badge/documentation-blue?style=for-the-badge)]({info['documentation_url']})\n" + if "support_url" in info: + readme += f"[![Support](https://img.shields.io/badge/support-orange?style=for-the-badge)]({info['support_url']})\n" + if "documentation_url" in info or "support_url" in info: + readme += "\n" + + if "description" in info: + readme += f"{info['description']}\n\n" + + if "supported_engines" in info and info["supported_engines"] is not None: + readme += f"> Supported engines: {', '.join(info['supported_engines'])}\n\n" + + return readme + + +def get_general_requirements(info: dict) -> str: + """ + Get the general requirements included in the info file. + + Args: + info: Info dict + + Returns: + Table with requirements + """ + requires_shotgun_version = "-" + if ( + "requires_shotgun_version" in info + and info["requires_shotgun_version"] is not None + ): + requires_shotgun_version = info["requires_shotgun_version"] + + requires_core_version = "-" + if "requires_core_version" in info and info["requires_core_version"] is not None: + requires_core_version = info["requires_core_version"] + + requires_engine_version = "-" + if ( + "requires_engine_version" in info + and info["requires_engine_version"] is not None + ): + requires_engine_version = info["requires_engine_version"] + + return make_table( + [ + "ShotGrid version", + "Core version", + "Engine version", + ], + [ + [ + requires_shotgun_version, + requires_core_version, + requires_engine_version, + ] + ], + ) + + +def capitalize(word: str) -> str: + """ + Capitalize the first letter of a word. + + Args: + word: String to capitalize + + Returns: + Capitalized string + """ + return word[0].upper() + word[1:] + + +def yaml_to_nested_dict(yaml_data: dict) -> dict: + """ + Convert dot separated yaml data to nested dicts. + + Args: + yaml_data: + + Returns: + Nested dict + """ + nested_dict = {} + for key, value in yaml_data.items(): + keys = key.split(".") + current_dict = nested_dict + for k in keys[:-1]: + current_dict = current_dict.setdefault(k, {}) + current_dict[keys[-1]] = value + return nested_dict + + +def make_table(cols: list[str], rows: list[list[str]]) -> str: + """ + Create a Markdown table + + Args: + cols: Header names + rows: Rows + + Returns: + Markdown table string + """ + + def sanitize(content: str) -> str: + if content is None: + return "" + return content.strip().replace("\n", "
").replace("|", "\\|") + + sizes = list(map(len, cols)) + for row in rows: + for i, item in enumerate(row): + item = sanitize(item) + if len(item) > sizes[i]: + sizes[i] = len(item) + + table_md = "" + for i, col in enumerate(cols): + content = sanitize(col) + table_md += f"| {content}{' ' * (sizes[i] - len(content) + 1)}" + table_md += "|\n" + + for i, col in enumerate(cols): + table_md += f"|{'-' * (sizes[i] + 2)}" + table_md += "|\n" + + for row in rows: + for i, item in enumerate(row): + content = sanitize(item) + table_md += f"| {content}{' ' * (sizes[i] - len(content) + 1)}" + table_md += "|\n" + return table_md + "\n" From aa5fc506a9539d6e6764333553b651e459c56451 Mon Sep 17 00:00:00 2001 From: MaximumFX Date: Fri, 6 Sep 2024 11:56:52 +0200 Subject: [PATCH 3/3] Fix override filepath variable --- tk-readme-generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tk-readme-generator.py b/tk-readme-generator.py index 620e2e7..c3e681e 100644 --- a/tk-readme-generator.py +++ b/tk-readme-generator.py @@ -84,8 +84,8 @@ def generate_readme( # Decide mode if not provided if mode is None: - filepath = filepath.parent / "env" / "includes" - if filepath.is_dir(): + includes_filepath = filepath.parent / "env" / "includes" + if includes_filepath.is_dir(): mode = Mode.config else: mode = Mode.general