#!/usr/bin/env python3 import argparse import os import subprocess import xml.etree.ElementTree as ET SCRIPTDIR = os.path.dirname(__file__) class Colors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' force_disable = False @classmethod def disable(cls): cls.HEADER = '' cls.OKBLUE = '' cls.OKGREEN = '' cls.WARNING = '' cls.FAIL = '' cls.ENDC = '' @classmethod def enable(cls): if cls.force_disable: return cls.HEADER = '\033[95m' cls.OKBLUE = '\033[94m' cls.OKGREEN = '\033[92m' cls.WARNING = '\033[93m' cls.FAIL = '\033[91m' cls.ENDC = '\033[0m' def git(args, repository_path): if not isinstance(args, list): args = [args] return subprocess.check_output(["git"] + args, cwd=repository_path, stderr=subprocess.STDOUT).decode() def manifest_get_commits(manifest): res = {} tree = ET.parse(manifest) root = tree.getroot() for child in root: if child.tag == 'project': res[child.attrib["name"]] = child.attrib["revision"] return res def update_subprojects(manifest, no_interaction=False): if manifest: repos_commits = manifest_get_commits(manifest) else: repos_commits = {} subprojects_dir = os.path.join(SCRIPTDIR, "subprojects") for repo_name in os.listdir(subprojects_dir): repo_dir = os.path.normpath(os.path.join(SCRIPTDIR, subprojects_dir, repo_name)) if not os.path.exists(os.path.join(repo_dir, '.git')): continue revision = repos_commits.get(repo_name) if not update_repo(repo_name, repo_dir, revision, no_interaction): return False return True def update_repo(repo_name, repo_dir, revision, no_interaction, recurse_i=0): print("Updating %s..." % repo_name) try: if revision: git(["fetch"], repo_dir) git(["checkout", revision], repo_dir) else: git(["pull", "--rebase"], repo_dir) except Exception as e: out = getattr(e, "output", b"").decode() if not no_interaction: print("=====================================" "\n%sEntering a shell in %s to fix that" " just `exit` once done`" "\n=====================================" % ( out, os.getcwd())) try: subprocess.check_call(os.environ.get("SHELL", "/bin/sh"), cwd=repo_dir) except: # Result of subshell does not really matter pass if recurse_i < 3: return update_repo(repo_name, repo_dir, revision, no_interaction, recurse_i + 1) return False else: print("\nCould not rebase %s, please fix and try again." " Error:\n\n%s %s" % (repo_dir, out, e)) return False commit_message = git("show", repo_dir).split("\n") print(u" -> %s%s%s — %s" % (Colors.HEADER, commit_message[0][7:14], Colors.ENDC, commit_message[4].strip())) return True if __name__ == "__main__": parser = argparse.ArgumentParser(prog="git-update") parser.add_argument("--no-color", default=False, action='store_true', help="Do not output ansi colors.") parser.add_argument("--no-interaction", default=False, action='store_true', help="Do not allow interaction with the user.") parser.add_argument("--manifest", default=None, help="Use a android repo manifest to sync repositories" " Note that it will let all repositories in detached state") options = parser.parse_args() if options.no_color: Colors.disable() exit(not update_subprojects(options.manifest, options.no_interaction))