gitmirror.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #!/usr/bin/env python3
  2. import os
  3. import sys
  4. from subprocess import run,Popen, PIPE
  5. local_base=f"./repositories"
  6. COM=sys.argv[0]
  7. def parser():
  8. from argparse import ArgumentParser
  9. p = ArgumentParser()
  10. p.add_argument("source")
  11. p.add_argument("-b", "--base", default="./repositories",help="local basedir for mirror")
  12. return p
  13. def main():
  14. p = parser()
  15. args = p.parse_args()
  16. repos = repos_from_lines(git_info(args.source))
  17. repo_obj = [Repo(args.source,r,args.base) for r in repos]
  18. for output in map_threaded(
  19. pull_repo,
  20. repo_obj,
  21. debug=lambda cur,tot,arg:print(f"{COM}: {cur}/{tot}: {arg}"),
  22. ):
  23. pass
  24. class Repo():
  25. def __init__(self,remote,path,base):
  26. self.remote = remote
  27. self.path = path
  28. self.base = base
  29. def source_uri(self):
  30. return f"{self.remote}:{self.path}"
  31. def local_path(self):
  32. return os.path.join(self.base, self.remote, self.path+".git")
  33. def __repr__(self):
  34. return f"Repo('{self.remote}','{self.path}','{self.base}')"
  35. def git_info(source_server):
  36. proc = Popen(
  37. ["ssh",source_server,"info"],
  38. stdout=PIPE,
  39. stderr=PIPE,
  40. encoding="utf-8")
  41. for line in iter(proc.stdout.readline, ""):
  42. yield line
  43. proc.wait()
  44. print("GIT_INFO DONE!!!!!!!!!!!!!!!")
  45. def repos_from_lines(lines):
  46. for line in lines:
  47. if line.find("gitolite") >= 0:
  48. continue
  49. fields = line.split()
  50. if len(fields) < 3:
  51. continue
  52. yield fields[2]
  53. def pull_repo(repo):
  54. if not os.path.isdir(os.path.join(repo.local_path() , "refs")):
  55. init_repo(repo.source_uri(), repo.local_path())
  56. update_local_repo(repo.local_path())
  57. return True
  58. '''
  59. :param target: A function to be mapped.
  60. :param args: An Array or Generator to be mapped.
  61. :return: Generator of function return values, but not in same order.
  62. '''
  63. def map_threaded(target, args=[], procs=4, debug=None):
  64. import threading
  65. import queue
  66. inq = queue.Queue()
  67. outq = queue.Queue()
  68. jobs = []
  69. def worker():
  70. while True:
  71. try:
  72. num, arg = inq.get(block=False)
  73. except:
  74. continue
  75. if num == None:
  76. break
  77. if debug:
  78. debug( num, num+inq.qsize(), arg)
  79. result = target(arg)
  80. outq.put(result)
  81. while len(jobs) < procs:
  82. th = threading.Thread(target=worker)
  83. th.start()
  84. jobs.append(th)
  85. def filler():
  86. i = 0
  87. for arg in args:
  88. i += 1
  89. inq.put((i,arg))
  90. for job in jobs:
  91. inq.put((None,None))
  92. fillThread = threading.Thread(target=filler)
  93. fillThread.start()
  94. while fillThread.is_alive():
  95. yield outq.get(block=True)
  96. fillThread.join()
  97. for job in jobs:
  98. job.join()
  99. while not outq.empty():
  100. yield outq.get(block=True)
  101. def update_local_repo(local_repo):
  102. proc = run(["git","fetch","-p","origin"], cwd=local_repo)
  103. if not proc.returncode == 0:
  104. print(f"Error: {proc}, {local_repo}")
  105. _rm(local_repo)
  106. def init_repo(source_repo, local_repo):
  107. _rm(local_repo)
  108. run(["mkdir","-p",local_repo])
  109. proc1 = run(["git","init","--bare"], cwd=local_repo)
  110. proc2 = run(["git","remote","add","origin",source_repo], cwd=local_repo)
  111. if not proc1.returncode == 0 and proc2.returncode == 0:
  112. print(f"Error: {proc1}, {proc2}, {local_repo}")
  113. _rm(local_repo)
  114. def _rm(di):
  115. run(["rm","-rf", di])
  116. if __name__ == "__main__":
  117. main()