Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ jobs:
run: git fetch origin ${{ github.base_ref }}

- name: Set up uv
uses: astral-sh/setup-uv@v3
with:
version: '0.5.4'
enable-cache: true
uses: astral-sh/setup-uv@v7

- name: Build and publish all modules
if: github.event_name != 'pull_request'
Expand Down Expand Up @@ -93,11 +90,11 @@ jobs:
fabric_server_url: https://meta.fabricmc.net/v2/versions/loader/1.21.5/0.16.13/1.0.3/server/jar
fabric_api_url: https://cdn.modrinth.com/data/P7dR8mSH/versions/FZ4q3wQK/fabric-api-0.119.9%2B1.21.5.jar
packtest_url: https://cdn.modrinth.com/data/XsKUhp45/versions/Swh7th09/packtest-2.1-mc1.21.5.jar
- version: '1.21.7'
- version: '1.21.7'
fabric_server_url: https://meta.fabricmc.net/v2/versions/loader/1.21.7/0.16.14/1.0.3/server/jar
fabric_api_url: https://cdn.modrinth.com/data/P7dR8mSH/versions/sLmbxWpX/fabric-api-0.128.1%2B1.21.7.jar
packtest_url: https://cdn.modrinth.com/data/XsKUhp45/versions/PAYctH3X/packtest-2.2-mc1.21.7.jar
- version: '1.21.8'
- version: '1.21.8'
fabric_server_url: https://meta.fabricmc.net/v2/versions/loader/1.21.7/0.16.14/1.0.3/server/jar
fabric_api_url: https://cdn.modrinth.com/data/P7dR8mSH/versions/sLmbxWpX/fabric-api-0.128.1%2B1.21.7.jar
packtest_url: https://cdn.modrinth.com/data/XsKUhp45/versions/PAYctH3X/packtest-2.2-mc1.21.7.jar
Expand Down
5 changes: 3 additions & 2 deletions base/beet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ version: 1.8.0
id: gm4

data_pack:
load:
load:
data: data

resource_pack:
resource_pack:
load:
assets: assets

Expand All @@ -30,3 +30,4 @@ meta:
babelbox:
load: assets/translations.csv
namespace: gm4_translations
dialect: excel
10 changes: 5 additions & 5 deletions gm4/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
from beet import Project
from beet.toolchain.cli import beet

# NOTE pydantic.v1 does not allow reloading models with custom validators, which beet watch will do normally.
# Importing them here prevents their reload on each watch cycle. This may change in pydantic.v2 - revisit then
# NOTE pydantic does not allow reloading models with custom validators, which beet watch will do normally.
# Importing them here prevents their reload on each watch cycle.
from gm4.utils import MapOption # type: ignore
from gm4.plugins.resource_pack import ModelData # type: ignore

Expand Down Expand Up @@ -104,16 +104,16 @@ def clean():
@click.option("-c", "--clean", is_flag=True, help="Clean the output folder.")
def readme_gen(ctx: click.Context, project: Project, modules: tuple[str, ...], watch: bool, clean: bool):
"""Generates all README files for manual uplaoad"""

modules = tuple(m if m.startswith("gm4_") else f"gm4_{m}" for m in modules)
if len(modules) == 0:
click.echo("[GM4] You need at least one module")
return

if clean:
click.echo(f"[GM4] Cleaning output folder...")
shutil.rmtree("out", ignore_errors=True)

click.echo(f"[GM4] Generating READMEs for: {', '.join(modules)}")

# we want to only read in the metadata from each project fo make a readme, not run the whole build pipeline
Expand Down
24 changes: 12 additions & 12 deletions gm4/plugins/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


def beet_default(ctx: Context):
"""Sets up a logging handlers to emit build log entries with the github action annotation format,
"""Sets up a logging handlers to emit build log entries with the github action annotation format,
and create a summary with useful build information."""
root_logger = logging.getLogger(None) # get root logger

Expand All @@ -28,7 +28,7 @@ def filter(record: logging.LogRecord):

root_logger.handlers.clear() # clear the handler set by beet CLI toolchain
root_logger.addHandler(ann_handler)

# summary handler holds onto certain records until the exit phase when it emits to a markdown summary
sum_handler = SummaryHandler(1000, ctx.cache)
logging.getLogger("gm4.output").addHandler(sum_handler)
Expand All @@ -47,12 +47,12 @@ def filter(record: logging.LogRecord):
}

class AnnotationFormatter(logging.Formatter):

def format(self, record: logging.LogRecord) -> str:

expl = record.getMessage().replace("\n", "%0A")
# use urlencoded newline

level = LEVEL_CONVERSION.get(record.levelno, LEVEL_CONVERSION[logging.INFO])

filename = None
Expand All @@ -61,14 +61,14 @@ def format(self, record: logging.LogRecord) -> str:

if getattr(record, "gh_annotate_skip", False): # disable annotations for any gm4 log events flagged manually
return f"{level.capitalize()}: {record.name} {expl}" # formatted to resemble annotated entry

match = re.match(r"(.+):(\d+):(\d+)", getattr(record, "annotate", "")) # extract filename and location from mecha annotation
if match:
filename, line, col = match.groups()
return f"::{level} file={filename},line={line},col={col},title={record.name}::{record.name} {expl}"

return f"::{level} title={record.name}::{record.name} {expl}"

class SummaryHandler(logging.handlers.BufferingHandler):
def __init__(self, capacity: int, beet_cache: ProjectCache):
super().__init__(capacity)
Expand All @@ -78,8 +78,8 @@ def __init__(self, capacity: int, beet_cache: ProjectCache):
def flush(self):
summary_entries: dict[str, Any] = {}

this_manifest = ManifestCacheModel.parse_obj(self.beet_cache["gm4_manifest"].json)
last_manifest = ManifestFileModel.parse_obj(self.beet_cache["previous_manifest"].json)
this_manifest = ManifestCacheModel.model_validate(self.beet_cache["gm4_manifest"].json)
last_manifest = ManifestFileModel.model_validate(self.beet_cache["previous_manifest"].json)

this_versions = {id: entry.version for id, entry in (this_manifest.modules | this_manifest.libraries).items()}
last_versions = {id: entry.version for id, entry in ({e.id: e for e in last_manifest.modules} | last_manifest.libraries).items()}
Expand Down Expand Up @@ -107,7 +107,7 @@ def flush(self):
"ver_update": f"{last_version_num} → {this_version_num}",
"logs": [] # list of tuples ("smithed", log_message)
}

# append to logs
summary_entries[module_id]["logs"].append((service, record.getMessage()))

Expand All @@ -120,7 +120,7 @@ def flush(self):
nested_table += "</table></details>"

table += f"\n {entry['name']} | {entry['ver_update']} | {nested_table}"

summary = "# :rocket: Build Deployment Summary :rocket:\n"+table

if not self.summary_created:
Expand All @@ -131,7 +131,7 @@ def flush(self):
self.summary_created = True
self.buffer.clear()



def add_module_dir_to_diagnostics(ctx: Context):
"""Sets up a logging record filter that prepends the proper module folder to mecha diagnostics"""
Expand All @@ -141,7 +141,7 @@ def add_module_dir_to_diagnostics(ctx: Context):

yield
mc_logger.removeFilter(local_filter) # clear the filter once done (after mecha)


def add_mecha_subproject_dir(record: logging.LogRecord, subproject_dir: str|Path = ""):
if d:=getattr(record, "annotate"):
Expand Down
2 changes: 1 addition & 1 deletion gm4/plugins/backwards.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def backport(pack: Pack[Any], format: int, run: Callable[[str, NamespaceFile], N

for overlay in pack.overlays.values():
overlay_formats = overlay.supported_formats or overlay.pack_format
if check_formats(overlay_formats, format):
if overlay_formats and check_formats(overlay_formats, format):
for file_type in overlay.resolve_scope_map().values():
proxy = overlay[file_type]
for path in proxy.keys():
Expand Down
40 changes: 20 additions & 20 deletions gm4/plugins/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from beet import Context, InvalidProjectConfig, PluginOptions, TextFile, load_config, Function
from beet.library.base import _dump_files # type: ignore ; private method used to deterministicify pack dumping
from nbtlib.contrib.minecraft import StructureFileData, StructureFile # type: ignore ; no stub
from pydantic.v1 import BaseModel, Extra
from pydantic import BaseModel
from repro_zipfile import ReproducibleZipFile # type: ignore ; no stub

from gm4.plugins.versioning import VersioningConfig
Expand Down Expand Up @@ -41,17 +41,17 @@ class SmithedConfig(PluginOptions):
class PMCConfig(PluginOptions):
uid: int

class ManifestConfig(PluginOptions, extra=Extra.ignore):
class ManifestConfig(PluginOptions, extra="ignore"):
minecraft: list[str] = SUPPORTED_GAME_VERSIONS
versioning: Optional[VersioningConfig]
versioning: Optional[VersioningConfig] = None
# distribution
website: Optional[WebsiteConfig]
modrinth: Optional[ModrinthConfig]
smithed: Optional[SmithedConfig]
pmc: Optional[PMCConfig]
website: Optional[WebsiteConfig] = None
modrinth: Optional[ModrinthConfig] = None
smithed: Optional[SmithedConfig] = None
pmc: Optional[PMCConfig] = None
# promo
video: str|None
wiki: str|None
video: str|None = None
wiki: str|None = None
credits: CreditsModel

# models for meta.json and cached manifest
Expand Down Expand Up @@ -102,7 +102,7 @@ def create(ctx: Context):
for pack_id in [p.name for p in sorted(ctx.directory.glob(glob))]:
try:
config = load_config(Path(pack_id) / "beet.yaml")
gm4_meta = ctx.validate("gm4", validator=ManifestConfig, options=config.meta["gm4"]) # manually parse config into models
gm4_meta = ctx.validate("gm4", validator=ManifestConfig, options=config.meta["gm4"]) # manually parse config into models

manifest_section[pack_id] = ManifestModuleModel(
id = config.id,
Expand Down Expand Up @@ -142,7 +142,7 @@ def create(ctx: Context):
manifest.base = {"version": base_config["version"]}

# Cache the new manifest, so sub-pipelines can access it
ctx.cache["gm4_manifest"].json = manifest.dict()
ctx.cache["gm4_manifest"].json = manifest.model_dump()

# Read in the previous manifest, if found
version = os.getenv("VERSION", "1.21.5")
Expand All @@ -158,21 +158,21 @@ def create(ctx: Context):
sys.exit(1) # quit the build and mark the github action as failed
else:
logger.warning("No existing meta.json manifest file was located")
ctx.cache["previous_manifest"].json = ManifestFileModel(last_commit="",modules=[],libraries={},contributors=[]).dict()
ctx.cache["previous_manifest"].json = ManifestFileModel(last_commit="",modules=[],libraries={},contributors=[]).model_dump()




def update_patch(ctx: Context):
"""Checks the datapack files for changes from last build, and increments patch number"""
yield
logger = parent_logger.getChild("update_patch")

# load current manifest from cache
this_manifest = ManifestCacheModel.parse_obj(ctx.cache["gm4_manifest"].json)
this_manifest = ManifestCacheModel.model_validate(ctx.cache["gm4_manifest"].json)
pack = ({e.id:e for e in (this_manifest.libraries|this_manifest.modules).values()})[ctx.project_id]

# attempt to load prior meta.json manifest
last_manifest = ManifestFileModel.parse_obj(ctx.cache["previous_manifest"].json)
last_manifest = ManifestFileModel.model_validate(ctx.cache["previous_manifest"].json)
released_modules: dict[str, ManifestModuleModel] = {m.id:m for m in last_manifest.modules if m.version}|{l.id:l for l in last_manifest.libraries.values()}

# determine this modules status
Expand Down Expand Up @@ -214,7 +214,7 @@ def update_patch(ctx: Context):
else: # no changes, keep the patch
pack.version = released.version

ctx.cache["gm4_manifest"].json = this_manifest.dict()
ctx.cache["gm4_manifest"].json = this_manifest.model_dump()


def write_meta(ctx: Context):
Expand All @@ -232,7 +232,7 @@ def write_meta(ctx: Context):

def write_credits(ctx: Context):
"""Writes the credits metadata to CREDITS.md. and collects for README.md"""
manifest = ManifestCacheModel.parse_obj(ctx.cache["gm4_manifest"].json)
manifest = ManifestCacheModel.model_validate(ctx.cache["gm4_manifest"].json)
contributors = manifest.contributors
module = manifest.modules.get(ctx.project_id)
credits = module.credits if module else {}
Expand All @@ -254,7 +254,7 @@ def write_credits(ctx: Context):
linked_credits[title].append(f"[{name}]({links[0]})")
else:
linked_credits[title].append(f"{name}")

# format credits for CREDITS.md
text = "# Credits\n"
for title in linked_credits:
Expand All @@ -277,7 +277,7 @@ def write_update_function(init: Optional[Function], ctx: Context):
if not init:
return

manifest = ManifestCacheModel.parse_obj(ctx.cache["gm4_manifest"].json)
manifest = ManifestCacheModel.model_validate(ctx.cache["gm4_manifest"].json)
modules = manifest.modules

score = f"{ctx.project_id.removeprefix('gm4_')} gm4_modules"
Expand Down Expand Up @@ -309,7 +309,7 @@ def write_update_function(init: Optional[Function], ctx: Context):

def repro_structure_to_bytes(content: StructureFileData) -> bytes:
"""a modified Structure.to_bytes from beet, which ensures the GZip does not add
the current time.time to the nbt file header.
the current time.time to the nbt file header.
Used for deterministic pack builds and auto-patch detection"""
dst = BytesIO()
with GzipFile(fileobj=dst, mode="wb", mtime=0) as fileobj:
Expand Down
16 changes: 8 additions & 8 deletions gm4/plugins/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def beet_default(ctx: Context):
out_dir = Path("out")

yield # wait for exit phase, after other plugins cleanup

ctx.data.save(
path=out_dir / f"{ctx.project_id}_{version.replace('.', '_')}",
overwrite=True,
Expand Down Expand Up @@ -73,7 +73,7 @@ def release(ctx: Context):
"""
Saves the zipped datapack and metadata to the ./release/{version} folder.
Should be first in pipeline to properly wrap all other plugins cleanup phases

If the module has the `version` and `meta.modrinth.project_id` fields, and
`BEET_MODRINTH_TOKEN` environment variable is set, will try to publish a
new version to Modrinth if it doesn't already exist.
Expand All @@ -90,7 +90,7 @@ def release(ctx: Context):
file_name = f"{corrected_project_id}_{version_dir.replace('.', '_')}.zip"

yield # wait for exit phase, after other plugins cleanup

ctx.data.save(
path=release_dir / file_name,
overwrite=True,
Expand All @@ -103,7 +103,7 @@ def release(ctx: Context):
os.makedirs(pack_icon_dir, exist_ok=True)
if "pack.png" in ctx.data.extra:
ctx.data.extra["pack.png"].dump(pack_icon_dir, f"{corrected_project_id}.png")

smithed_readme_dir = generated_dir / "smithed_readmes"
os.makedirs(smithed_readme_dir, exist_ok=True)
if "smithed_readme" in ctx.meta:
Expand Down Expand Up @@ -200,7 +200,7 @@ def publish_smithed(ctx: Context, config: ManifestConfig, file_name: str):
auth_token = os.getenv(SMITHED_AUTH_KEY, None)
logger = parent_logger.getChild(f"smithed.{ctx.project_id}")
mc_version_dir = os.getenv("VERSION", "1.21.5")
manifest = ManifestCacheModel.parse_obj(ctx.cache["gm4_manifest"].json)
manifest = ManifestCacheModel.model_validate(ctx.cache["gm4_manifest"].json)
project_id = stem if (stem:=ctx.directory.stem).startswith("lib") else ctx.project_id

if config.smithed and auth_token:
Expand All @@ -214,7 +214,7 @@ def publish_smithed(ctx: Context, config: ManifestConfig, file_name: str):
else:
logger.warning(f"Failed to get project: {res.status_code} {res.text}")
return

project_data = res.json()

# update description and pack image
Expand Down Expand Up @@ -307,10 +307,10 @@ def clear_release(ctx: Context):

def readmes(ctx: Context):
"""Saves all READMEs intended for download sites to the ./out/readmes folder."""

readme_dir = Path("out/readmes")
base_path = readme_dir / ctx.project_id

if "README.md" in ctx.data.extra:
os.makedirs(base_path, exist_ok=True)
ctx.data.extra["README.md"].dump(base_path, "GM4_README.md")
Expand Down
5 changes: 2 additions & 3 deletions gm4/plugins/prefabs.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
from beet import Context, PluginOptions, configurable
from beet.contrib.find_replace import find_replace
from beet.contrib.rename_files import rename_files
from pydantic.v1 import Extra
import nbtlib # type: ignore ; no stub file
import re
from gm4.plugins.manifest import repro_structure_to_bytes


class PrefabConfig(PluginOptions, extra=Extra.ignore):
class PrefabConfig(PluginOptions, extra="ignore"):
prefabs: list[str]

def beet_default(ctx: Context):
"""Handles renaming of prefab assets as they merge into a for modules use"""
prefab_namespace = ctx.project_id
module_namsepace = ctx.cache["currently_building"].json["id"]

structure_deep_rename(ctx, prefab_namespace, module_namsepace)

@configurable("gm4", validator=PrefabConfig)
Expand Down
Loading