mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-11-19 00:04:53 +00:00
246 lines
7.8 KiB
Python
Executable File
246 lines
7.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""
|
|
The main build script for CC: Tweaked.
|
|
|
|
This sets up work trees for each branch, ensures they are up-to-date with the
|
|
remotes and then merges the primary branch (mc-1.15.x) into each other branch.
|
|
|
|
We then build each branch, push changes, and upload all versions.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import os.path
|
|
import pathlib
|
|
import regex
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import xml.etree.ElementTree as ET
|
|
from dataclasses import dataclass
|
|
from textwrap import dedent
|
|
from typing import List, Optional
|
|
|
|
|
|
@dataclass
|
|
class Branch:
|
|
name: str
|
|
parent: Optional[str] = None
|
|
java: str = "1.8"
|
|
|
|
|
|
BRANCHES: List[Branch] = [
|
|
Branch('mc-1.16.x'),
|
|
Branch('mc-1.17.x', parent='mc-1.16.x', java="16"),
|
|
# Branch('mc-fabric-1.17.x', parent='mc-1.17.x', java="16"),
|
|
]
|
|
|
|
|
|
def log(msg: str, *args) -> None:
|
|
"""Print a format string to the console"""
|
|
print("\033[32m" + msg % tuple(args) + "\033[0m")
|
|
|
|
|
|
def git_in(branch: Branch, *cmd: str, **kwargs) -> subprocess.CompletedProcess:
|
|
"""Run a git command on a specific branch."""
|
|
return subprocess.run(["git", "-C", branch.name, *cmd], **kwargs)
|
|
|
|
|
|
def run_in(branch: Branch, *cmd: str, **kwargs) -> subprocess.CompletedProcess:
|
|
"""Run a command within the context of a specific branch."""
|
|
return subprocess.run(cmd, cwd=branch.name, **kwargs)
|
|
|
|
|
|
def setup() -> None:
|
|
"""
|
|
Setup the repository suitable for working with multiple versions. Namely:
|
|
- Register all remotes.
|
|
- Copy gradle files from the default branch.
|
|
"""
|
|
# Update git remote.
|
|
log("Updating from remotes")
|
|
subprocess.check_call(["git", "fetch", "origin"])
|
|
|
|
# Setup git repository.
|
|
for branch in BRANCHES:
|
|
if not os.path.isdir(branch.name):
|
|
log(f"Creating worktree for {branch.name}")
|
|
subprocess.check_call(["git", "worktree", "add", branch.name, branch.name])
|
|
|
|
# Setup gradle.
|
|
log("Setting up gradle project")
|
|
for file in ["gradle", "gradlew", "gradlew.bat"]:
|
|
src = f"{BRANCHES[0].name}/{file}"
|
|
if os.path.isdir(src):
|
|
shutil.copytree(src, file, dirs_exist_ok=True)
|
|
else:
|
|
shutil.copy2(src, file)
|
|
|
|
with open("settings.gradle", "w") as h:
|
|
h.write(dedent("""\
|
|
pluginManagement {
|
|
repositories {
|
|
maven {
|
|
name = 'Fabric'
|
|
url = 'https://maven.fabricmc.net/'
|
|
}
|
|
gradlePluginPortal()
|
|
}
|
|
}
|
|
|
|
rootProject.name = 'cc-tweaked'
|
|
"""))
|
|
for branch in BRANCHES:
|
|
h.write(f"include '{branch.name}'\n")
|
|
|
|
with open("build.gradle", "w") as h:
|
|
pass
|
|
|
|
|
|
def gen_runs() -> None:
|
|
"""
|
|
Generate .idea run files
|
|
"""
|
|
setup()
|
|
subprocess.check_call(["./gradlew", "--no-daemon", "genIntellijRuns"])
|
|
|
|
re = regex.compile(r"(mc-.*)_run(.*)\.xml")
|
|
for path in pathlib.Path(".idea/runConfigurations").glob("*.xml"):
|
|
group = re.match(path.name)
|
|
if not group:
|
|
continue
|
|
version, name = group[1], group[2]
|
|
|
|
for branch in BRANCHES:
|
|
if branch.name == version:
|
|
break
|
|
else:
|
|
print(f"Cannot find Java for version for {path}")
|
|
continue
|
|
|
|
component = 'testMod' if name.startswith('Test') else 'main'
|
|
xml = ET.parse(path)
|
|
|
|
# Put run configurations in folders
|
|
configuration = xml.find("./configuration")
|
|
if configuration:
|
|
configuration.set("folderName", version)
|
|
|
|
# Ensure they're linked to the right CC:T version
|
|
module = xml.find("./configuration/module")
|
|
if module is None:
|
|
print("Unknown module for " + path.name)
|
|
else:
|
|
module.set("name", f"cc-tweaked.{version}.{component}")
|
|
|
|
# Force a specific JDK version
|
|
version = xml.find("./configuration/option[@name='ALTERNATIVE_JRE_PATH']")
|
|
if version:
|
|
version.set("value", branch.java)
|
|
else:
|
|
root = xml.find("./configuration")
|
|
assert root
|
|
ET.SubElement(root, 'option', {'name': 'ALTERNATIVE_JRE_PATH', 'value': branch.java})
|
|
ET.SubElement(root, 'option', {'name': 'ALTERNATIVE_JRE_PATH_ENABLED', 'value': 'true'})
|
|
|
|
xml.write(path)
|
|
|
|
|
|
def check_git() -> None:
|
|
"""
|
|
Check all worktrees are in a sensible state prior to merging.
|
|
"""
|
|
setup()
|
|
|
|
# Ensure every worktree is on the right branch, has no uncommited changes and is
|
|
# up-to-date with the remote.
|
|
ok = True
|
|
for branch in BRANCHES:
|
|
status = git_in(branch, "status", "--porcelain", check=True, stdout=subprocess.PIPE).stdout
|
|
if len(status.strip()) > 0:
|
|
log(f"{branch.name} has changes. Build will not continue.")
|
|
ok = False
|
|
continue
|
|
|
|
actual_branch = git_in(branch, "rev-parse", "--abbrev-ref", "HEAD", check=True, stdout=subprocess.PIPE).stdout.decode("utf-8").strip()
|
|
if actual_branch != branch.name:
|
|
log(f"{branch.name} is actually on {actual_branch} right now")
|
|
ok = False
|
|
continue
|
|
|
|
if git_in(branch, "merge-base", "--is-ancestor", f"origin/{branch.name}", branch.name).returncode != 0:
|
|
log(f"{branch.name} is not up-to-date with remote.")
|
|
ok = False
|
|
continue
|
|
|
|
if not ok:
|
|
sys.exit(1)
|
|
|
|
|
|
def build() -> None:
|
|
"""
|
|
Merge in parent branches, then build all branches.
|
|
"""
|
|
check_git()
|
|
|
|
# Merge each branch into the next one.
|
|
for branch in BRANCHES:
|
|
if (
|
|
branch.parent is not None and
|
|
git_in(branch, "merge-base", "--is-ancestor", branch.parent, branch.name).returncode != 0
|
|
):
|
|
log(f"{branch.name} is not up-to-date with {branch.parent}.")
|
|
ret = git_in(branch, "merge", "--no-edit", branch.parent).returncode
|
|
if ret != 0:
|
|
log(f"Merge {branch.parent} -> {branch.name} failed. Aborting.")
|
|
sys.exit(ret)
|
|
|
|
log("Git state is up-to-date. Preparing to build - you might want to make a cup of tea.")
|
|
|
|
for branch in BRANCHES:
|
|
log(f"Building {branch.name}")
|
|
ret = run_in(branch, "xvfb-run", "./gradlew", "build", "--no-daemon").returncode
|
|
if ret != 0:
|
|
log(f"Build failed")
|
|
sys.exit(ret)
|
|
|
|
|
|
def release() -> None:
|
|
"""Publish releases for each version."""
|
|
build()
|
|
|
|
check = input("Are you sure you want to release? Make sure you've performed some manual checks first! [y/N]").lower()
|
|
if check != "y":
|
|
sys.exit(1)
|
|
|
|
subprocess.check_call(["git", "push", "origin", *(b.name for b in BRANCHES)])
|
|
for branch in BRANCHES:
|
|
log(f"Uploading {branch.name}")
|
|
ret = run_in(branch, "./gradlew", "uploadAll", "--no-daemon").returncode
|
|
if ret != 0:
|
|
log(f"Upload failed. Good luck in recovering from this!")
|
|
sys.exit(ret)
|
|
|
|
log(f"Finished. Well done, you may now enjoy your tea!")
|
|
|
|
|
|
def main() -> None:
|
|
# Validate arguments.
|
|
parser = argparse.ArgumentParser(description="Build scripts for CC: Tweaked")
|
|
subparsers = parser.add_subparsers(
|
|
description="The subcommand to run. Subcommands implicitly run their dependencies.",
|
|
dest="subcommand",
|
|
required=True,
|
|
)
|
|
subparsers.add_parser("setup", help="Setup the git repository and build environment.").set_defaults(func=setup)
|
|
subparsers.add_parser("gen-runs", help="Generate IntelliJ IDEA run configurations.").set_defaults(func=gen_runs)
|
|
subparsers.add_parser("check-git", help="Check the git worktrees are in a state ready for merging.").set_defaults(func=check_git)
|
|
subparsers.add_parser("build", help="Merge and build all branches.").set_defaults(func=build)
|
|
subparsers.add_parser("release", help="Publish a release.").set_defaults(func=release)
|
|
|
|
parser.parse_args().func()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|