diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..65012edf2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: Run CI + +on: + push: + branches: main + +permissions: + contents: read + +concurrency: + group: ci + cancel-in-progress: false + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install Nox + run: pip install nox + + - name: Run tests + run: nox diff --git a/README.md b/README.md index 0cb7a0ef0..649710b47 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ hexdoc logo

Docs - hexdoc.hexxy.media + GitHub Workflow Status (with event) PyPI - Version PyPI - Python Version - GitHub deployments + Pydantic Version 2

# hexdoc diff --git a/cookiecutter.json b/cookiecutter.json index 06126e816..91da05012 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -2,9 +2,15 @@ "output_directory": "", "modid": "", + "book_id": "hexcasting:thehexbook", "mod_display_name": "{{ cookiecutter.modid|capitalize }}", "plugin_classname": "{{ cookiecutter.modid|capitalize }}Plugin", + "gradle_mod_version_key": "modVersion", + "multiloader": true, + "java_lang": ["java", "kotlin"], + "java_package": "com/example/{{ cookiecutter.modid }}", + "pattern_registry": "registry/{{ cookiecutter.modid|capitalize }}PatternRegistry.{{ 'java' if cookiecutter.java_lang == 'java' else 'kt' }}", "github_repo": "{{ cookiecutter.modid|capitalize }}", "author": "TODO", diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 000000000..e0b4ca9e8 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,19 @@ +import nox + + +@nox.session +def lint(session: nox.Session): + session.install("favicons") # TODO: remove + session.install(".[test]", "nox", "pyright") + + session.run("pyright", "--warnings") + + +@nox.session +def tests(session: nox.Session): + session.install("favicons") # TODO: remove + session.install(".[test]", "./test/_submodules/HexMod") + + # test cookiecutter last so the extra package install doesn't interfere + session.run("pytest", "-k", "not test_cookiecutter") + session.run("pytest", "-k", "test_cookiecutter") diff --git a/pyproject.toml b/pyproject.toml index bbb0851a6..b77ce4b14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,7 @@ dev = [ "black==23.7.0", "isort==5.12.0", "hatch", - "build", + "nox", ] [project.urls] diff --git a/src/hexdoc/minecraft/assets/textures.py b/src/hexdoc/minecraft/assets/textures.py index fc8a339f4..15195d475 100644 --- a/src/hexdoc/minecraft/assets/textures.py +++ b/src/hexdoc/minecraft/assets/textures.py @@ -86,7 +86,7 @@ def find( for missing_id in props.textures.missing: for id in ids: if id.match(missing_id): - logging.getLogger(__name__).warn(message) + logging.getLogger(__name__).warning(message) return Texture(file_id=id, url=MISSING_TEXTURE) raise KeyError(message) diff --git a/test/test_cookiecutter.py b/test/test_cookiecutter.py index bfecdeed1..5f2e9b46d 100644 --- a/test/test_cookiecutter.py +++ b/test/test_cookiecutter.py @@ -1,21 +1,117 @@ # pyright: reportUnknownMemberType=false +import json +import subprocess +import sys +from pathlib import Path +from textwrap import dedent + from pytest import MonkeyPatch from pytest_cookies.plugin import Cookies +from hexdoc._cli.app import render + +from .conftest import longrun + +@longrun def test_cookiecutter(cookies: Cookies, monkeypatch: MonkeyPatch): result = cookies.bake( { "output_directory": "output", "modid": "mod", "pattern_regex": "hex_latest", + "multiloader": False, + "java_package": "com/package", + "pattern_registry": "Patterns.java", } ) assert result.exception is None assert result.project_path is not None + monkeypatch.chdir(result.project_path) + subprocess.run(["git", "init"], check=True) + + Path("gradle.properties").write_text( + dedent( + f"""\ + modVersion=1.0.0 + hexcastingVersion=0.11.1-7 + minecraftVersion=1.20.1 + """ + ) + ) + + java_root = Path("src/main/java/com/package") + java_root.mkdir(parents=True) + (java_root / "Patterns.java").touch() + + Path("src/generated/resources").mkdir(parents=True) + + book_root = Path( + "src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us" + ) + book_root.mkdir(parents=True) + + category_root = book_root / "categories" + category_root.mkdir(parents=True) + with open(category_root / "foo.json", "w") as f: + json.dump( + { + "name": "hexdoc.mod.title", + "icon": "minecraft:amethyst_shard", + "description": "hexcasting.category.basics.desc", + "sortnum": 0, + }, + f, + ) + + entry_root = book_root / "entries" + (entry_root / "foo").mkdir(parents=True) + with open(entry_root / "foo" / "bar.json", "w") as f: + json.dump( + { + "name": "hexdoc.welcome.header", + "category": "hexcasting:foo", + "icon": "minecraft:textures/mob_effect/nausea.png", + "sortnum": 0, + "advancement": "hexcasting:y_u_no_cast_angy", + "pages": [ + { + "type": "patchouli:text", + "text": "hexcasting.page.couldnt_cast.1", + }, + ], + }, + f, + ) + + (result.project_path / ".env").write_text( + dedent( + f"""\ + GITHUB_REPOSITORY=GITHUB/REPOSITORY + GITHUB_SHA=GITHUB_SHA + GITHUB_PAGES_URL=GITHUB_PAGES_URL""" + ) + ) + + # TODO: remove when textures stop being broken + with open(result.project_path / "doc" / "properties.toml", "a") as f: + f.write( + dedent( + """ + [textures] + missing = [ + "minecraft:*", + "hexcasting:*", + ] + """ + ) + ) + monkeypatch.syspath_prepend(result.project_path / "doc" / "src") - import hexdoc_mod # type: ignore + subprocess.run([sys.executable, "-m", "pip", "install", "-e", "."]) + + render() diff --git a/{{cookiecutter.output_directory}}/doc/static/icon.png b/{{cookiecutter.output_directory}}/doc/icon.png similarity index 100% rename from {{cookiecutter.output_directory}}/doc/static/icon.png rename to {{cookiecutter.output_directory}}/doc/icon.png diff --git a/{{cookiecutter.output_directory}}/doc/nodemon.json b/{{cookiecutter.output_directory}}/doc/nodemon.json index d136273b7..abf20eb06 100644 --- a/{{cookiecutter.output_directory}}/doc/nodemon.json +++ b/{{cookiecutter.output_directory}}/doc/nodemon.json @@ -3,7 +3,7 @@ "doc/src", "doc/resources", "doc/properties.toml", - "Common/src/main/resources/assets/*/lang" + "{{ 'common/src' if cookiecutter.multiloader else 'src' }}/main/resources/assets/*/lang" ], "ignore": ["**/generated/**"], "ext": "jinja,html,css,js,ts,toml,json,json5,py", diff --git a/{{cookiecutter.output_directory}}/doc/properties.toml b/{{cookiecutter.output_directory}}/doc/properties.toml index 6722b777a..18d99da1e 100644 --- a/{{cookiecutter.output_directory}}/doc/properties.toml +++ b/{{cookiecutter.output_directory}}/doc/properties.toml @@ -1,65 +1,73 @@ modid = "{{ cookiecutter.modid }}" -book = "hexcasting:thehexbook" +book = "{{ cookiecutter.book_id }}" default_lang = "en_us" -_export_root = "{{ cookiecutter.__export_root }}" resource_dirs = [ # top takes priority - { path="{^_export_root}/resources", reexport=false }, + "resources", "{_common.src}/main/resources", "{_common.src}/generated/resources", + {% if cookiecutter.multiloader -%} "{_fabric.src}/main/resources", "{_fabric.src}/generated/resources", "{_forge.src}/main/resources", "{_forge.src}/generated/resources", + {%- endif %} { modid="hexcasting" }, + { modid="hexdoc" }, ] -export_dir = "{_export_root}/generated" +export_dir = "{{ cookiecutter.__export_root }}/generated" -{# beware of eldritch abombinations lurking beneath these waters -#} +[extra.hexcasting] +# regexes for parsing pattern registry files - try uncommenting a different one if your patterns aren't loading # NOTE: "!Raw" means "don't apply variable interpolation to this value" -{% if cookiecutter.pattern_regex == "hex_latest" -%} -_pattern_regex = { "!Raw"='make\(\s*"(?P[a-zA-Z0-9_\/]+)",\s*(?:new )?(?:ActionRegistryEntry|OperationAction)\(\s*HexPattern\.fromAngles\(\s*"(?P[aqweds]+)",\s*HexDir.(?P\w+)\)' } -{% elif cookiecutter.pattern_regex == "hex_0.10.3" -%} -_pattern_regex = { "!Raw"='HexPattern\.fromAngles\("(?P[qweasd]+)", HexDir\.(?P\w+)\),\s*modLoc\("(?P[^"]+)"\)[^;]+?(?:makeConstantOp|Op\w+|Widget\.\w+)(?:[^;]*(?Ptrue)\);)?' } -{% elif cookiecutter.pattern_regex == "hexal_0.3.0" -%} -_pattern_regex = { "!Raw"='make\(\s*"(?P[a-zA-Z0-9_\/]+)",\s*HexPattern\.fromAngles\(\s*"(?P[aqweds]+)",\s*HexDir.(?P\w+)\)' } -{% elif cookiecutter.pattern_regex == "hexal_0.2.18" -%} -{#- :yea: -#} -_pattern_regex = { "!Raw"='(?s-m:HexPattern\.fromAngles\("(?P[qweasd]+)", HexDir\.(?P\w+)\),\s*modLoc\("(?P[^"]+)"\),[^,]+?(?:makeConstantOp|Op\w+).*?(?P\btrue)?\)(?:[^\)]+?\bval\b|(?:(?!\bval\b)(?:.))+$))' } -{% else %} -{# intentionally crash the template because we got an unhandled value #} -{{ 0/0 }} -{% endif %} -[[pattern_stubs]] -path = "{^_common.package}/TODO/TODO.java" +# Hex Casting (0.11.0) +{{ "# " if cookiecutter.pattern_regex != "hex_latest" }}_pattern_regex = { "!Raw"='make\(\s*"(?P[a-zA-Z0-9_\/]+)",\s*(?:new )?(?:ActionRegistryEntry|OperationAction)\(\s*HexPattern\.fromAngles\(\s*"(?P[aqweds]+)",\s*HexDir.(?P\w+)\)' } + +# Hex Casting (0.10.3) +{{ "# " if cookiecutter.pattern_regex != "hex_0.10.3" }}_pattern_regex = { "!Raw"='HexPattern\.fromAngles\("(?P[qweasd]+)", HexDir\.(?P\w+)\),\s*modLoc\("(?P[^"]+)"\)[^;]+?(?:makeConstantOp|Op\w+|Widget\.\w+)(?:[^;]*(?Ptrue)\);)?' } + +# Hexal (0.3.0) +{{ "# " if cookiecutter.pattern_regex != "hexal_0.3.0" }}_pattern_regex = { "!Raw"='make\(\s*"(?P[a-zA-Z0-9_\/]+)",\s*HexPattern\.fromAngles\(\s*"(?P[aqweds]+)",\s*HexDir.(?P\w+)\)' } + +# Hexal (0.2.18) +{{ "# " if cookiecutter.pattern_regex != "hexal_0.2.18" }}_pattern_regex = { "!Raw"='(?s-m:HexPattern\.fromAngles\("(?P[qweasd]+)", HexDir\.(?P\w+)\),\s*modLoc\("(?P[^"]+)"\),[^,]+?(?:makeConstantOp|Op\w+).*?(?P\btrue)?\)(?:[^\)]+?\bval\b|(?:(?!\bval\b)(?:.))+$))' } + +[[extra.hexcasting.pattern_stubs]] +path = "{^^^_common.package}/{{ cookiecutter.pattern_registry }}" regex = "{^_pattern_regex}" +[minecraft_assets] +# https://github.com/PrismarineJS/minecraft-assets/tree/83e2169afbbce40990d69fc53e5962e4a793d467/data/1.19.1 +ref = "83e2169afbbce40990d69fc53e5962e4a793d467" +version = "1.19.1" + [template] -static_dir = "static" +icon = "icon.png" include = [ "{{ cookiecutter.modid }}", "hexcasting", "patchouli", + "hexdoc", ] [template.args] mod_name = "{{ cookiecutter.mod_display_name }}" author = "{{ cookiecutter.author }}" -icon_href = "icon.png" show_landing_text = false - # platforms [_common] -src = "../Common/src" -package = "{src}/main/java/TODO/{{ cookiecutter.modid }}" +src = "../{{ 'common/src' if cookiecutter.multiloader else 'src' }}" +package = "{src}/main/{{ cookiecutter.java_lang }}/{{ cookiecutter.java_package }}" +{% if cookiecutter.multiloader -%} [_fabric] -src = "../Fabric/src" -package = "{src}/main/java/TODO/{{ cookiecutter.modid }}/fabric" +src = "../fabric/src" +package = "{src}/main/{{ cookiecutter.java_lang }}/{{ cookiecutter.java_package }}/fabric" [_forge] -src = "../Forge/src" -package = "{src}/main/java/TODO/{{ cookiecutter.modid }}/forge" +src = "../forge/src" +package = "{src}/main/{{ cookiecutter.java_lang }}/{{ cookiecutter.java_package }}/forge" +{%- endif %} diff --git a/{{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_export/resources/assets/hexcasting/lang/en_us.flatten.json5 b/{{cookiecutter.output_directory}}/doc/resources/assets/hexcasting/lang/en_us.flatten.json5 similarity index 100% rename from {{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_export/resources/assets/hexcasting/lang/en_us.flatten.json5 rename to {{cookiecutter.output_directory}}/doc/resources/assets/hexcasting/lang/en_us.flatten.json5 diff --git a/{{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_export/resources/__init__.py b/{{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_export/resources/__init__.py deleted file mode 100644 index d7a48f963..000000000 --- a/{{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_export/resources/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# You can add extra resources in this directory for hexdoc to load. -# For example, hexdoc uses this for translations which are only needed for the web book. diff --git a/{{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_hooks.py b/{{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_hooks.py index 80be7478c..3813adcdb 100644 --- a/{{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_hooks.py +++ b/{{cookiecutter.output_directory}}/doc/src/{{cookiecutter.__project_slug}}/_hooks.py @@ -5,32 +5,39 @@ LoadJinjaTemplatesImpl, LoadResourceDirsImpl, ModVersionImpl, + MinecraftVersionImpl, hookimpl, ) import {{ cookiecutter.__project_slug }} -from .__gradle_version__ import GRADLE_VERSION +from .__gradle_version__ import GRADLE_VERSION, MINECRAFT_VERSION class {{ cookiecutter.plugin_classname }}( LoadJinjaTemplatesImpl, LoadResourceDirsImpl, ModVersionImpl, + MinecraftVersionImpl, ): @staticmethod @hookimpl def hexdoc_mod_version() -> str: return GRADLE_VERSION + @staticmethod + @hookimpl + def hexdoc_minecraft_version() -> str: + return MINECRAFT_VERSION + @staticmethod @hookimpl def hexdoc_load_resource_dirs() -> HookReturn[Package]: # This needs to be a lazy import because they may not exist when this file is # first loaded, eg. when generating the contents of generated. - from ._export import generated, resources + from ._export import generated - return [generated, resources] + return generated @staticmethod @hookimpl