mirror of
https://github.com/frida/releng.git
synced 2026-11-07 09:36:02 -10:00
188 lines
6.9 KiB
Python
188 lines
6.9 KiB
Python
import os
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
def make_gnome_url(repo_name):
|
|
return "https://gitlab.gnome.org/GNOME/{}.git".format(repo_name)
|
|
|
|
|
|
upstreams = {
|
|
"meson": ("https://github.com/mesonbuild/meson.git", "master"),
|
|
"termux-elf-cleaner": "https://github.com/termux/termux-elf-cleaner.git",
|
|
"libiconv": "https://git.savannah.gnu.org/git/libiconv.git",
|
|
"zlib": ("https://github.com/madler/zlib.git", "develop"),
|
|
"brotli": "https://github.com/google/brotli.git",
|
|
"minizip": "https://github.com/zlib-ng/minizip-ng.git",
|
|
"libffi": "https://github.com/libffi/libffi.git",
|
|
"libunwind": "https://github.com/libunwind/libunwind.git",
|
|
"glib": make_gnome_url("glib"),
|
|
"glib-networking": (make_gnome_url("glib-networking"), "master"),
|
|
"libnice": "https://gitlab.freedesktop.org/libnice/libnice.git",
|
|
"usrsctp": "https://github.com/sctplab/usrsctp.git",
|
|
"libgee": make_gnome_url("libgee"),
|
|
"json-glib": make_gnome_url("json-glib"),
|
|
"libpsl": "https://github.com/rockdaboot/libpsl.git",
|
|
"libxml2": make_gnome_url("libxml2"),
|
|
"libsoup": make_gnome_url("libsoup"),
|
|
"vala": make_gnome_url("vala"),
|
|
"libdwarf": "https://github.com/davea42/libdwarf-code.git",
|
|
"xz": "https://git.tukaani.org/xz.git",
|
|
"pkg-config": "https://gitlab.freedesktop.org/pkg-config/pkg-config.git",
|
|
"quickjs": ("https://github.com/bellard/quickjs.git", "master"),
|
|
"gn": "https://gn.googlesource.com/gn",
|
|
"v8": "https://chromium.googlesource.com/v8/v8",
|
|
"capstone": ("https://github.com/capstone-engine/capstone.git", "v5"),
|
|
"tinycc": "https://repo.or.cz/tinycc.git",
|
|
"selinux": "https://github.com/SELinuxProject/selinux.git",
|
|
}
|
|
|
|
|
|
def sync(repo_path):
|
|
repo_name = os.path.basename(repo_path)
|
|
|
|
patches_path = os.path.join(str(Path.home()), ".frida-sync-" + re.sub(r"[^\w\d]", "-", repo_path.lower()).lstrip("-"))
|
|
if os.path.exists(patches_path):
|
|
patches = PendingPatches.load(patches_path)
|
|
|
|
print("Applying {} pending patches".format(patches.count))
|
|
else:
|
|
entry = upstreams.get(repo_name, None)
|
|
if entry is None:
|
|
raise UnknownUpstreamError("Unknown upstream: {}".format(repo_name))
|
|
if isinstance(entry, tuple):
|
|
upstream_url, upstream_branch = entry
|
|
else:
|
|
upstream_url = entry
|
|
upstream_branch = "main"
|
|
upstream_target = "upstream/" + upstream_branch
|
|
|
|
print("Synchronizing with {}".format(upstream_url))
|
|
|
|
subprocess.run(["git", "checkout", "main"], cwd=repo_path, capture_output=True, check=True)
|
|
subprocess.run(["git", "pull"], cwd=repo_path, capture_output=True, check=True)
|
|
result = subprocess.run(["git", "status"], cwd=repo_path, capture_output=True, check=True, encoding='utf-8')
|
|
if not "working tree clean" in result.stdout:
|
|
raise WorkingTreeDirtyError("Working tree is dirty")
|
|
|
|
subprocess.run(["git", "remote", "add", "upstream", upstream_url], cwd=repo_path, capture_output=True)
|
|
subprocess.run(["git", "fetch", "upstream"], cwd=repo_path, check=True)
|
|
|
|
patches, base = list_our_patches(repo_path)
|
|
print("We have {} patches on top of upstream".format(patches.count))
|
|
|
|
new_entries = list_upstream_changes(repo_path, upstream_target, base)
|
|
if len(new_entries) == 0:
|
|
print("Already up-to-date")
|
|
return
|
|
|
|
print("Upstream has {} new commits".format(len(new_entries)))
|
|
|
|
print("Merging...")
|
|
subprocess.run(["git", "merge", "-s", "ours", upstream_target], cwd=repo_path, capture_output=True, check=True)
|
|
subprocess.run(["git", "checkout", "--detach", upstream_target], cwd=repo_path, capture_output=True, check=True)
|
|
subprocess.run(["git", "reset", "--soft", "main"], cwd=repo_path, capture_output=True, check=True)
|
|
subprocess.run(["git", "checkout", "main"], cwd=repo_path, capture_output=True, check=True)
|
|
subprocess.run(["git", "commit", "--amend", "-C", "HEAD"], cwd=repo_path, capture_output=True, check=True)
|
|
|
|
patches.save(patches_path)
|
|
|
|
while True:
|
|
index, cid, message = patches.try_pop()
|
|
if index is None:
|
|
break
|
|
|
|
print("Cherry-picking {}/{}: {}".format(index + 1, patches.count, message))
|
|
try:
|
|
subprocess.run(["git", "cherry-pick", cid], cwd=repo_path, capture_output=True, encoding='utf-8', check=True)
|
|
except subprocess.CalledProcessError as e:
|
|
patches.save(patches_path)
|
|
|
|
print("\n*** Unable to apply this patch:")
|
|
print(e.stderr)
|
|
print("Run `git cherry-pick --abort` and re-run script to skip it.")
|
|
|
|
return
|
|
|
|
os.remove(patches_path)
|
|
print("Done!")
|
|
|
|
def list_our_patches(repo_path):
|
|
items = []
|
|
base = None
|
|
entries = list_recent_commits(repo_path, "--max-count=1000")
|
|
for index, entry in enumerate(entries):
|
|
cid, message = entry
|
|
if message.startswith("Merge"):
|
|
base = entries[index + 1][0]
|
|
break
|
|
items.append(("pending", cid, message))
|
|
items.reverse()
|
|
return (PendingPatches(items), base)
|
|
|
|
def list_upstream_changes(repo_path, upstream_target, since):
|
|
return list(reversed(list_recent_commits(repo_path, since + ".." + upstream_target)))
|
|
|
|
def list_recent_commits(repo_path, *args):
|
|
result = subprocess.run(["git", "log", "--pretty=oneline", "--abbrev-commit", "--topo-order"] + list(args),
|
|
cwd=repo_path, capture_output=True, check=True, encoding='utf-8', errors='surrogateescape')
|
|
return [line.split(" ", 1) for line in result.stdout.rstrip().split("\n")]
|
|
|
|
|
|
class PendingPatches(object):
|
|
def __init__(self, items):
|
|
self._items = items
|
|
|
|
offset = 0
|
|
for status, cid, message in items:
|
|
if status == "applied":
|
|
offset += 1
|
|
else:
|
|
break
|
|
self._offset = offset
|
|
|
|
@property
|
|
def count(self):
|
|
return len(self._items)
|
|
|
|
def try_pop(self):
|
|
index = self._offset
|
|
if index == len(self._items):
|
|
return (None, None, None)
|
|
|
|
_, cid, message = self._items[index]
|
|
self._items[index] = ("applied", cid, message)
|
|
self._offset += 1
|
|
|
|
return (index, cid, message)
|
|
|
|
@classmethod
|
|
def load(cls, path):
|
|
with open(path, "r", encoding='utf-8') as f:
|
|
data = f.read()
|
|
|
|
items = []
|
|
for line in data.strip().split("\n"):
|
|
status, cid, message = line.split(" ", maxsplit=2)
|
|
items.append((status, cid, message))
|
|
return PendingPatches(items)
|
|
|
|
def save(self, path):
|
|
data = "\n".join([" ".join(item) for item in self._items]) + "\n"
|
|
with open(path, "w", encoding='utf-8') as f:
|
|
f.write(data)
|
|
|
|
|
|
class WorkingTreeDirtyError(Exception):
|
|
pass
|
|
|
|
|
|
class UnknownUpstreamError(Exception):
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sync(os.path.abspath(sys.argv[1]))
|