2021-01-09 18:25:24 +00:00
|
|
|
#!/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 subprocess
|
|
|
|
import shutil
|
|
|
|
import sys
|
|
|
|
import os.path
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from typing import List, Tuple, Optional
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Branch:
|
|
|
|
name: str
|
|
|
|
parent: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
BRANCHES: List[Branch] = [
|
|
|
|
Branch('mc-1.15.x'),
|
|
|
|
Branch('mc-1.16.x', parent='mc-1.15.x'),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
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("rootProject.name = 'cc-tweaked'\n")
|
|
|
|
for branch in BRANCHES:
|
|
|
|
h.write(f"include '{branch.name}'\n")
|
|
|
|
|
|
|
|
with open("build.gradle", "w") as h:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def build() -> None:
|
|
|
|
"""
|
|
|
|
Ensure the git repository is in a clean state.
|
|
|
|
"""
|
|
|
|
setup()
|
|
|
|
|
|
|
|
# Ensure every tree is clean.
|
|
|
|
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
|
|
|
|
if not ok:
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
# Ensure every tree is up-to-date.
|
|
|
|
primary = BRANCHES[0]
|
|
|
|
for branch in BRANCHES:
|
|
|
|
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.")
|
|
|
|
# TODO: We should possibly attempt to merge/rebase here instead of aborting.
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
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, "./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)
|
|
|
|
|
2021-01-09 21:41:39 +00:00
|
|
|
subprocess.check_call(["git", "push", "origin", *(b.name for b in BRANCHES)])
|
2021-01-09 18:25:24 +00:00
|
|
|
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("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()
|