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
|
|
|
|
|
|
|
|
|
2021-01-15 10:18:22 +00:00
|
|
|
def check_git() -> None:
|
2021-01-09 18:25:24 +00:00
|
|
|
"""
|
2021-01-15 10:18:22 +00:00
|
|
|
Check all worktrees are in a sensible state prior to merging.
|
2021-01-09 18:25:24 +00:00
|
|
|
"""
|
|
|
|
setup()
|
|
|
|
|
2021-01-15 10:18:22 +00:00
|
|
|
# Ensure every worktree is on the right branch, has no uncommited changes and is
|
|
|
|
# up-to-date with the remote.
|
2021-01-09 18:25:24 +00:00
|
|
|
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
|
2021-01-15 10:18:22 +00:00
|
|
|
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
|
2021-01-09 18:25:24 +00:00
|
|
|
|
|
|
|
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.")
|
2021-01-15 10:18:22 +00:00
|
|
|
ok = False
|
|
|
|
continue
|
|
|
|
|
|
|
|
if not ok:
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
def build() -> None:
|
|
|
|
"""
|
|
|
|
Merge in parent branches, then build all branches.
|
|
|
|
"""
|
|
|
|
check_git()
|
2021-01-09 18:25:24 +00:00
|
|
|
|
2021-01-15 10:18:22 +00:00
|
|
|
# Merge each branch into the next one.
|
|
|
|
for branch in BRANCHES:
|
2021-01-09 18:25:24 +00:00
|
|
|
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)
|
2021-01-15 10:18:22 +00:00
|
|
|
subparsers.add_parser("check-git", help="Check the git worktrees are in a state ready for merging.").set_defaults(func=check_git)
|
2021-01-09 18:25:24 +00:00
|
|
|
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()
|